Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purpose of django.db.models.fields.Field.name argument

Recently i discovered that there is not-documented django.db.models.fields.Field.name option:

@total_ordering
class Field(RegisterLookupMixin):  #   here we have it
    ...                                   ↓↓↓↓↓↓↓↓↓
    def __init__(self, verbose_name=None, name=None, primary_key=False,
                 max_length=None, unique=False, blank=False, null=False,
                 db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
                 serialize=True, unique_for_date=None, unique_for_month=None,
                 unique_for_year=None, choices=None, help_text='', db_column=None,
                 db_tablespace=None, auto_created=False, validators=(),
                 error_messages=None):
        ...

There is mention of it in doc-way:

# A guide to Field parameters:
#
#   * name:      The name of the field specified in the model.
#   * attname:   The attribute to use on the model object. This is the same as
#                "name", except in the case of ForeignKeys, where "_id" is
#                appended.
#   * db_column: The db_column specified in the model (or None).
#   * column:    The database column for this field. This is the same as
#                "attname", except if db_column is specified.
#
# Code that introspects values, or does other dynamic things, should use
# attname. For example, this gets the primary key value of object "obj":
#
#     getattr(obj, opts.pk.attname)

Description above is related with #683 ([patch] Saving with custom db_column fails) ticket.

So if we look through whole django.db.models.fields.Field class, this seems as name option is setting attribute name, which make real name of variable invalid:

Suppose we have our model:

# models.py
from django.db import models


class SomeModel(models.Model):
    first = models.CharField(max_length=50, verbose_name='first', name='second')
    third = models.CharField(max_length=50, verbose_name='third')

What django-admin shell tells us:

In[2]: from app.models import SomeModel
In[3]: SomeModel.objects.create(first='first', third='third')
Traceback (most recent call last):
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2963, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-3-08e446dfd6e3>", line 1, in <module>
    SomeModel.objects.create(first='first', third='third')
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/django/db/models/query.py", line 415, in create
    obj = self.model(**kwargs)
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/django/db/models/base.py", line 495, in __init__
    raise TypeError("'%s' is an invalid keyword argument for this function" % kwarg)
TypeError: 'first' is an invalid keyword argument for this function
In[4]: obj = SomeModel.objects.create(second='second', third='third')
In[5] obj.third
Out[5]: 'third'
In[6]: obj.second
Out[6]: 'second'
In[7]: obj.first
Traceback (most recent call last):
  File "/Users/ailove/Home/personal/untitled/venv/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2963, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-7-f0deaec10795>", line 1, in <module>
    obj.first
AttributeError: 'SomeModel' object has no attribute 'first'

Question is kinda broad, but i am also curious.

Is this name option is a thing that only helped to develop django, or ordinary developers can also make use of it? And if we can, what for?

like image 223
vishes_shell Avatar asked Jul 25 '18 08:07

vishes_shell


People also ask

What is field name in Django?

Django uses field class types to determine a few things: The column type, which tells the database what kind of data to store (e.g. INTEGER, VARCHAR, TEXT). The default HTML widget to use when rendering a form field (e.g. <input type=”text”>, <select>).

What is required field in Django model?

How to use required in Django Form field? required is often used to make the field optional that is the user would no longer be required to enter the data into that field and it will still be accepted.

What is the use of related name in Django models?

The related_name attribute specifies the name of the reverse relation from the User model back to your model. If you don't specify a related_name, Django automatically creates one using the name of your model with the suffix _set.

What is the purpose of __ Str__ method in Django?

str function in a django model returns a string that is exactly rendered as the display name of instances for that model. # Create your models here. This will display the objects as something always in the admin interface.


1 Answers

I've found name useful if I want a model's field to have a getter and setter and hide the naming convention introduced by the getter/setter from the Django ORM and the database.

A fairly common pattern in Python is to have the getter and setter be named after the public name of the field, and have the field that holds the value of the field start with an underscore which by convention indicates that it is private. So for instance you'd have a setter and getter named foo and the "private" field for it named _foo:

class Something(object):
    _foo = "some default value"

    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, val):
        self._foo = val

The code above is barebones. Presumably, in a real-world scenario you'd have additional code in your getter or setter to do some additional work. (Otherwise, there's no reason for the getter and setter.) Assuming an instance of the class above named instance, you access instance.foo and you do not touch instance._foo because the _foo field is not part of the public API.

If you want to take the pattern above and implement it on a Django model you could just do this:

class MyModel(models.Model):
    _foo = models.TextField()

    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, val):
        self._foo = val

However, the net result is that your field is known to the Django ORM as _foo and it is stored in a column named _foo in the database. Some people will be okay with this, but in my projects I prefer that the existence of the getter/setter in Python not affect the name of the field elsewhere. In order to have the same name in the Django ORM and for the column name, you can do:

_foo = models.TextField(name="foo")

Doing this will set the name of the field as seen in the Django ORM, so this works:

MyModels.objects.get(foo=...)

Otherwise, you'd have to use the underscore and do MyModels.objects.get(_foo=...). And it also sets the name of the database column so in raw SQL you'd access the column as foo. If you happen to want a different column name, you have to use the db_column argument to set the name: models.TextField(name="foo", db_column="something_else")

like image 187
Louis Avatar answered Oct 22 '22 10:10

Louis