2
\$\begingroup\$

I'm currently in the process of refactoring a Django app to use Django REST Framework. Within the existing code there are 15 models for lookup tables. The structure of each model is as follows (using gender as an example - there are valid reasons in the context of the application for including a gender model rather than as hardcoded CHOICES on a Person model):

class Gender(models.Model):
    """
    This model stores genders
    """
    name = models.CharField(max_length=64)
    sort = models.IntegerField()

    class Meta:
        ordering = ['sort']

    def __unicode__(self):
        return self.name

(I've already made some changes that are included in the above code to bring it in line with Django's coding style guidelines)

All of the 15 models use the same field names that I am concerned should be more explicit.

My concern with using the name field-name is that it does not explicitly state what data is in the relevant table.

To add to this, I might have the following code in my views:

class PersonByGenderList(generics.ListAPIView):
    """
    This endpoint lists all people by gender
    """
    serializer_class = PersonSerializer

    def get_queryset(self):
        """
        :return: queryset of people restricted by gender.
        """
        queryset = Person.objects.all()
        gender = self.request.QUERY_PARAMS.get('gender', None)
        if gender is not None:
            queryset = queryset.filter(gender__name=gender)
        return queryset

Again, gender__name (for me) is not explicit enough. Also, in a SlugRelatedField the slug name is always slug_name='name'.

So my questions are:

  1. Are there any database design conventions that recommend using name as a generic field name on models, or should I be more explicit (following the Zen of Python) and prefer (albeit with some repetition):

    queryset = queryset.filter(gender__gender=gender)

    Or is it preferable to use name and be explicit with the model name in the case of gender?

  2. Is the arbitrary integer field used for sorting explicit enough when effectively it will sort by first entry to last? I would think it is generally better to sort by something that is relevant to the use case, but as with question 1 are there any conventions/style guides I should be aware of when naming fields?

Some info on my research:

I've looked for other questions on this site and Stack Overflow, Django's coding style guidelines, PEP8 but not found anything that directly answers this question. A question on Programmer's SE comes closest Should model/table names be as specific as possible. The points I take from the answers are:

naming should be as specific as possible

Table and field names should be descriptive of the domain entity that they capture

\$\endgroup\$
1
\$\begingroup\$

You could have an abstract base model to lock in how the lookup tables all should work:

class LookupModel(models.Model):
    name = models.CharField(max_length=64)
    sort = models.IntegerField()

    class Meta:
        abstract = True
        ordering = ['sort']

    def __unicode__(self):
        return self.name

.. ditto for a DRF serializer, you just swap out which model is being connected. When you do this you cannot go the "gender__gender=gender" route though. It's a matter of how lazy you are, really (as in laziness, impatience, hubris). One advantage is that you can have a single class in the admin if you use that:

class LookupAdmin(admin.ModelAdmin):
    list_display = ('name', sort')
    ...

admin.site.register(Gender, LookupAdmin)
admin.site.register(Location, LookupAdmin)
admin.site.register(Status, LookupAdmin)

As for naming conventions, if sort is chosen/changeable and an integer I would call it position. If a string, sortkey or something like that. If automatic: Django, when sorting on foreign key with order_with_respect_to, adds the invisible column "_order", so you could do that, and find a way to set the column automatically. Or, you could sort on the automatically added auto-incrementing primary keyid-field (which I admit I've never done). I don't like implicitness though so would have a manually added column and manually set "ordering". Fewer surprises that way, laziness again.

But, and here's why I write this in 2020, long after you are hopefully done with the project you needed input on: Django 3.0 can use enums in Field.choices. So, in your case, with just a single name-field and a sort you won't change, you could forego the lookup table and instead:


LifeStatus =  models.IntegerChoices(
    'LifeStatus',
    'ALIVE MISSING DEAD UNDEAD'
)

class Human(models.Model):
    status = models.IntegerField(choices=LifeStatus.choices)
    ..

You can then do things like:

# zombieapocalypse.py

if human_looking.status == LifeStatus.UNDEAD:
    run_away()

Which IMO is very readable. Typos would also be a syntax error, bonus!

(Unfortunately this doesn't fix my problem though, I'd like a lookup table that also works like an enum...)

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.