简体   繁体   中英

Django complicated string representation of a model object (def __str__(self):)

I have been using str to handle human readable names for model objects for awhile now, but recently I have been running into some strange crashes and bugs that seem to be related to this function.

I have two models SensorAssignment and SnComplex , both have OneToOne relationships to a column on a third model RadioSn . RadioSn is the master pool of all serial numbers. SnComplex is the list of serials assigned to a customer and SensorAssignment handles the metadata for a customer's serial assigned to a specific physical location.

It is important to note that this is a legacy db that I inherited. Some relationships are not ideal IMHO.

Below are simplified versions of the models with only relevant columns.

RadioSn

class RadioSn(models.Model):
    sn = models.AutoField(primary_key=True)
    role = models.ForeignKey('Roles', db_column='role', on_delete=models.DO_NOTHING)

    class Meta:
        managed = False
        db_table = 'radio_sn'
        ordering = ['sn']

    def __str__(self):
        return '%s -- %s' % (self.sn, self.role)

SnComplex

class SnComplex(models.Model):
sn = models.OneToOneField(RadioSn, on_delete=models.DO_NOTHING, db_column='sn', primary_key=True)
complex = models.ForeignKey(Complex, on_delete=models.DO_NOTHING, db_column='complex')

class Meta:
    managed = False
    db_table = 'sn_complex'

SensorAssignment

class SensorAssignment(models.Model):
unit = models.ForeignKey('Unit', on_delete=models.DO_NOTHING, db_column='unit')
sn = models.OneToOneField(RadioSn, on_delete=models.DO_NOTHING, db_column='sn')

class Meta:
    managed = False
    db_table = 'sensor_assignment'
    ordering = ['sn']

def __str__(self):
    return self.pk

Here is where we start to run into my problem. I use form.ModelForm to handle the creation and maintenance of SensorAssignment objects. Thus for the field sn it creates a html <select> with choices of RadioSn objects which are represented by the str format delineated above (a string composed of the int primary key sn and the foreign key role [bad naming IMHO]).

This works perfectly (I do limit the querysets to relevant options on the form's initialization on a view, so it isn't all() objects in the model.)

图片工作

So I should be able to do the same thing for SnComplex , right? After all each model has the same relationship to RadioSn on their respective sn columns, and the str representation of RadioSn objects is done on that model.

Nope, this is what I get.

错误 使其崩溃的行

If I simply represent RadioSn objects as self.sn instead of concatenating self.sn and self.role it works on both forms, but if I leave it concatenated is crashes the SnComplex form. All RadioSn objects have a role value.

Any ideas what is going on? We have a string representation of RadioSn model objects crashing the SnComplex ModelForm on template rendering, but working fine on multiple versions SensorAssignment ModelForm elsewhere.

Requested SnComplex form:

class AddSensorForm(forms.ModelForm):
    class Meta:
        model = SnComplex
        fields = '__all__'

Full traceback:

Request Method: GET
Request URL: http://127.0.0.1/complex/33/

Django Version: 2.0.4
Python Version: 3.6.1
Installed Applications:
['apps.dashboard',
 'rest_framework',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'apps.dashboard.templatetags.custom_filters',
 'widget_tweaks',
 'phonenumber_field',
 'pygal',
 'django_filters']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template C:\git\si-dash\apps\dashboard\templates\header.html, error at line 0
   Roles matching query does not exist.
   1 : {% load static %}
   2 : {% load widget_tweaks %}
   3 : {% load custom_filters %}
   4 : {% csrf_token %}
   5 : 
   6 : <!DOCTYPE html>
   7 : <html lang="en">
   8 : 
   9 : <head>
   10 :     <meta charset="utf-8">


Traceback:

File "C:\Python36\lib\site-packages\django\db\models\fields\related_descriptors.py" in __get__
  158.             rel_obj = self.field.get_cached_value(instance)

File "C:\Python36\lib\site-packages\django\db\models\fields\mixins.py" in get_cached_value
  13.             return instance._state.fields_cache[cache_name]

During handling of the above exception ('role'), another exception occurred:

File "C:\Python36\lib\site-packages\django\core\handlers\exception.py" in inner
  35.             response = get_response(request)

File "C:\Python36\lib\site-packages\django\core\handlers\base.py" in _get_response
  128.                 response = self.process_exception_by_middleware(e, request)

File "C:\Python36\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Python36\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "C:\git\si-dash\apps\dashboard\views\complex.py" in complex
  179.     return render(request, 'design/complex.html', passed_data)

File "C:\Python36\lib\site-packages\django\shortcuts.py" in render
  36.     content = loader.render_to_string(template_name, context, request, using=using)

File "C:\Python36\lib\site-packages\django\template\loader.py" in render_to_string
  62.     return template.render(context, request)

File "C:\Python36\lib\site-packages\django\template\backends\django.py" in render
  61.             return self.template.render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render
  175.                     return self._render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in _render
  167.         return self.nodelist.render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render
  943.                 bit = node.render_annotated(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render_annotated
  910.             return self.render(context)

File "C:\Python36\lib\site-packages\django\template\loader_tags.py" in render
  155.             return compiled_parent._render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in _render
  167.         return self.nodelist.render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render
  943.                 bit = node.render_annotated(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render_annotated
  910.             return self.render(context)

File "C:\Python36\lib\site-packages\django\template\loader_tags.py" in render
  67.                 result = block.nodelist.render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render
  943.                 bit = node.render_annotated(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render_annotated
  910.             return self.render(context)

File "C:\Python36\lib\site-packages\django\template\defaulttags.py" in render
  314.                 return nodelist.render(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render
  943.                 bit = node.render_annotated(context)

File "C:\Python36\lib\site-packages\django\template\base.py" in render_annotated
  910.             return self.render(context)

File "C:\Python36\lib\site-packages\django\template\defaulttags.py" in render
  211.                     nodelist.append(node.render_annotated(context))

File "C:\Python36\lib\site-packages\django\template\base.py" in render_annotated
  910.             return self.render(context)

File "C:\Python36\lib\site-packages\widget_tweaks\templatetags\widget_tweaks.py" in render
  187.         return str(bounded_field)

File "C:\Python36\lib\site-packages\django\utils\html.py" in <lambda>
  380.     klass.__str__ = lambda self: mark_safe(klass_str(self))

File "C:\Python36\lib\site-packages\django\forms\boundfield.py" in __str__
  36.         return self.as_widget()

File "C:\Python36\lib\site-packages\widget_tweaks\templatetags\widget_tweaks.py" in as_widget
  31.         html = old_as_widget(widget, attrs, only_initial)

File "C:\Python36\lib\site-packages\widget_tweaks\templatetags\widget_tweaks.py" in as_widget
  31.         html = old_as_widget(widget, attrs, only_initial)

File "C:\Python36\lib\site-packages\widget_tweaks\templatetags\widget_tweaks.py" in as_widget
  31.         html = old_as_widget(widget, attrs, only_initial)

File "C:\Python36\lib\site-packages\django\forms\boundfield.py" in as_widget
  118.             **kwargs

File "C:\Python36\lib\site-packages\django\forms\widgets.py" in render
  234.         context = self.get_context(name, value, attrs)

File "C:\Python36\lib\site-packages\django\forms\widgets.py" in get_context
  677.         context = super().get_context(name, value, attrs)

File "C:\Python36\lib\site-packages\django\forms\widgets.py" in get_context
  637.         context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs)

File "C:\Python36\lib\site-packages\django\forms\widgets.py" in optgroups
  585.         for index, (option_value, option_label) in enumerate(self.choices):

File "C:\Python36\lib\site-packages\django\forms\models.py" in __iter__
  1141.             yield self.choice(obj)

File "C:\Python36\lib\site-packages\django\forms\models.py" in choice
  1147.         return (self.field.prepare_value(obj), self.field.label_from_instance(obj))

File "C:\Python36\lib\site-packages\django\forms\models.py" in label_from_instance
  1213.         return str(obj)

File "C:\git\si-dash\apps\dashboard\models.py" in __str__
  728.         return '%s -- %s' % (self.sn, self.role)

File "C:\Python36\lib\site-packages\django\db\models\fields\related_descriptors.py" in __get__
  164.                 rel_obj = self.get_object(instance)

File "C:\Python36\lib\site-packages\django\db\models\fields\related_descriptors.py" in get_object
  139.         return qs.get(self.field.get_reverse_related_filter(instance))

File "C:\Python36\lib\site-packages\django\db\models\query.py" in get
  403.                 self.model._meta.object_name

Exception Type: DoesNotExist at /complex/33/
Exception Value: Roles matching query does not exist.

I was reviewing the issue you are having and I thought it might be an internal joining error. I looked at the DB (non django managed) and it appears the radio_sn.role is not set as a foreign key.

Given that it was possible to have a "bad" role associated with a particular sensor. It looks there was a legacy record of a serial number with a role that didn't exist in the roles table which was the root cause of the join error and the "no role" error you were getting.

I corrected those records and will look into steps into making radio_sn.role a foreign key to the roles table.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM