Nice pythonic way to specify django model field choices with extra attributes and methods

帅比萌擦擦* 提交于 2019-12-13 00:23:25

问题


How can you specify choices on a django model such that the "choice" carries more information than just the database value and display value (as required by django's choices spec)? Suppose the different choices have a number of config options that I want to set (in code) as well as some methods, and the methods may be different between different choices. Here's an example:

class Reminder(models.Model):
    frequency = models.CharField(choices=SOME_CHOICES)
    next_reminder = models.DateTimeField()
    ...

How should we specify SOME_CHOICES for something like "weekly" and "monthly" reminders? So far my best solution is to write a class for each frequency choice, and store the class name in the database and then import the class by name whenever I need the methods or config data.

Ideally I would like to specify all of these config values and define the methods for each choice all in one place, rather than have the logic scattered all over a bunch of Model methods with long if/elif/elif...else structures. Python is object-oriented... these frequency choices seem to have "data" and "methods" so they seem like good candidates for classes...

class ReminderFrequency(object):
    pass

class Weekly(ReminderFrequency):
    display_value = "Every week"

    def get_next_reminder_time(self):
        return <now + 7 days>

class Monthly(ReminderFrequency):
    display_value = "Every month"

    def get_next_reminder_time(self):
        return <now + 1 month>

SOME_CHOICES = ((freq.__name__, freq.display_value) for freq in [Weekly, Monthly])

And suppose that in addition to get_next_reminder_time, I want to specify something like first_reminder (let's say for "weekly" your first reminder comes three days from now, and the next one 7 days after that, but for "monthly", the first reminder comes 7 days from now, and the next one month after that, etc). Plus 5 other config values or methods that depend on the choice.

One thought is to make frequency a FK to some other model Frequency where I set the config values, but that does not allow different choices to have different methods or logic (like the weekly vs monthly example). So that's out.

My current approach feels very cumbersome because of the requirement to load each ReminderFrequency subclass by name according to the class name stored in the database. Any other ideas? Or is this "the Right and Good Pythonic Way"?


回答1:


I think the most natural way of handling this in Django would be to create a custom model field. You would use it like so:

class Reminder(models.Model):
    frequency = models.FrequencyField()
    next_reminder = models.DateTimeField()

reminder = Reminder.objects.get()
reminder_time = reminder.frequency.get_next_reminder_time()

To implement it, review the relevant documentation. Briefly, you'd probably:

  • Inherit from CharField
  • Supply the choices in the field definition
  • Implement get_prep_value(). You could represent values as actual class names, like you have above, or by some other value and use a lookup table.
  • Implement to_python(). This is where you'll convert the database representation into an actual Python instance of your classes.

It's a little more involved than that, but not much.

(The above assumes that you want to define the behavior in code. If you need to configure the behavior by supplying configuration values to the database (as you suggested above with the ForeignKey idea) that's another story.)



来源:https://stackoverflow.com/questions/24746426/nice-pythonic-way-to-specify-django-model-field-choices-with-extra-attributes-an

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!