Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a django model field with the same name as a Python keyword

I need to define a Django model field with the name in, which is a Python language keyword. This is a syntax error:

class MyModel(models.Model):
    in = jsonfield.JSONField()

How can I make this work?

The reason I need this name is when I use django-rest-framework's ModelSerializer class, field name is used as the key for serialization output, and I thought it might be easier to manipulate django's Model class instead of ModelSerializer class to get the output I want.

like image 364
zaadeh Avatar asked Dec 11 '22 19:12

zaadeh


1 Answers

Generally speaking, you don't. Avoid the use of keywords in your identifiers. The general Python convention is to add an underscore to such names; here that'd be in_:

class MyModel(models.Model):
    in_ = jsonfield.JSONField()

However, Django prohibits names ending in an underscore because the underscore clashes with their filter naming conventions, so you have to come up with a different name still; pick one that still describes your case; I picked contained in rather than in, as a guess to what you want to do here:

class MyModel(models.Model):
    contained_in = jsonfield.JSONField()

If you are trying to match an existing database schema, use the db_column attribute:

class MyModel(models.Model):
    contained_in = jsonfield.JSONField(db_column='in')

If you want to be stubborn, in normal classes you could use setattr() after creating the class to use a string instead of an identifier:

class Foo:
    pass

setattr(Foo, 'in', 'some value')

but you'll have to use setattr(), getattr(), delattr() and/or vars() everywhere in your code to be able to access this.

In Django you'll have the added complication that a models.Model subclass uses a metaclass to parse out your class members into others structures, and adding an extra field with setattr() doesn't work without (a lot of) extra work to re-do what the metaclass does. You could instead use the field.contribute_to() method, calling it after the class has been prepared by Django (technique taken from this blog post):

from django.db.models.signals import class_prepared

def add_field(sender, **kwargs):
    if sender.__name__ == "MyModel":
        field = jsonfield.JSONField('in')
        field.contribute_to_class(sender, 'in')

class_prepared.connect(add_field)

but you have to make sure this hook is registered before you create your model class.

like image 97
Martijn Pieters Avatar answered May 19 '23 17:05

Martijn Pieters