Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model fields from constants?

How to define a Django model field in constant and use everywhere. For example, if I have a model like:-

class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=40)
    email = models.EmailField()

And what I want to do is define constant for fields in Author model and provide the constant instead of field name in model like:-

KEY_FIRST_NAME = 'first_name'
KEY_LAST_NAME = 'last_name'
KEY_EMAIL = 'email'

And Author model should use the constant instead of exact key like:-

class Author(models.Model):
        KEY_FIRST_NAME = models.CharField(max_length=30)
        KEY_LAST_NAME = models.CharField(max_length=40)
        KEY_EMAIL = models.EmailField()

How to do something like this, direct assignment to constant won't work here.

I want to store all the field name in constant, and everywhere when it required I want to use the constant instead of string field name.

The purpose of doing this is If there is any change in filed name in future version then I want to only change at one place and it should reflect on all the places.

If it is not possible or it will make code too complex as suggested by one approach by @dirkgroten than what can be the best practice to define the model field as constant and use them in other places (other than inside models like if we are referring those field for admin portal or any other place).

like image 948
VikasGoyal Avatar asked Sep 05 '17 11:09

VikasGoyal


1 Answers

Short answer: you can't do this in Python, period (actually I don't think you could do so in any language but someone will certainly prove me wrong xD).

Now if we go back to your real "problem" - not having to change client code if your model's fields names are ever to change - you'd first need to tell whether you mean "the python attribute name" or "the underlying database field name".

For the second case, the database field name does not have to match the Python attribute name, Django models fields take a db_column argument to handle this case.

For the first case, I'd have to say that it's a very generic (and not new by any mean) API-design problem, and the usual answer is "you shouldn't change names that are part of your public API once it's been released". Now sh!t happens and sometimes you have to do it. The best solution here is then to use computed attributes redirecting the old name to the new one for the deprecation period and remove them once all the client code has been ported.

An example with your model, changing 'first_name' to 'firstname':

class Author(models.Model):
    # assuming the database column name didn't change
    # so we can also show how to us `db_column` ;)

    firstname = models.CharField(
        max_length=30, 
        db_column='first_name'
        )

    @property
    def first_name(self): 
        # shoud issue a deprecation warning here
        return self.firstname

    @first_name.setter
    def first_name(self, value):
        # shoud issue a deprecation warning here
        self.firstname = value

If you have a dozen fields to rename you will certainly want to write a custom descriptor (=> computed attribute) instead to keep it dry:

class Renamed(object):
    def __init__(self, new_name):
        self.new_name = new_name

    def __get__(self, instance, cls):
        if instance is None:
            return self

        # should issue a deprecation warning here
        return getattr(instance, self.new_name)

    def __set__(self, instance, value):
        # should issue a deprecation warning here
        setattr(instance, self.new_name, value)


class Author(models.Model):
    firstname = models.CharField(
        max_length=30, 
        db_column='first_name'
        )
    first_name = Renamed("firstname")
like image 99
bruno desthuilliers Avatar answered Oct 19 '22 19:10

bruno desthuilliers