简体   繁体   中英

How to have extra django model fields depending on the value of a field?

I have a model in my Django project called Job . Each Job has a category. An example of a category could be tutoring . This can be represented as what my model looks like right now:

from __future__ import unicode_literals

from django.db import models

class Job(models.Model):

    # Abbreviations for possible categories to be stored in the database.
    TUTORING = "TU"
    PETSITTING = "PS"
    BABYSITTING = "BS"
    INTERIOR_DESIGN = "IND"
    SHOPPING = "SH"
    SOFTWARE_DEVELOPMENT = "SD"
    DESIGN = "DE"
    ART = "AR"
    HOUSEKEEPING = "HK"
    OTHER = "OT"

    JOB_CATEGORY_CHOICES = (
        (TUTORING, 'Tutoring'),
        (PETSITTING, "Petsitting"),
        (BABYSITTING, "Babysitting"),
        (INTERIOR_DESIGN, "Interior Design"),
        (SHOPPING, "Shopping"),
        (SOFTWARE_DEVELOPMENT, "Software Development"),
        (DESIGN), "Design"),
        (ART, "Art"),
        (HOUSEKEEPING, "Housekeeping"),
        (OTHER, "Other"),
    )

    created_at = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=255)
    description = models.TextField()
    category = models.CharField(max_length=3, choices=JOB_CATEGORY_CHOICES, default=OTHER,)

    def __str__(self):
        return self.title

Depending on the category of the Job, different fields are required. For example, if I take tutoring as the category again, then extra fields like address , subject , level of study and others are needed. If the category of the Job is software development however, extra fields like project_size and required_qualifications are needed.

Should I create a separate model for each type of Job or is there some kind of model inheritance I can use where job types inherit from the main Job model which holds all the common fields that all Jobs need.

Essentially, what is the best way to have extra fields depending on the Job category?

You have some options:

1. OneToOneField on various category models:

  • Pro: allows other models to have FK to Job model. Eg you could retrieve all of a person jobs via person.jobs.all() no matter which category.

  • Con: Allows instances of different categories to relate to the same Job instance: Extra work is needed to maintain data integrity

    More tables, more joins, slower queries

    Adding a category always entails a migration!

2. Multi-Table inheritance:

Uses OneToOneField under the hood.

  • Pro: as above + but each instance of a category will autocreate its own Job instance, so no collisions between categories.

  • Con: More tables, more joins, slower queries. Obscures some of the db stuff that's going on.

    Adding a category always entails a migration!

3. Job as an abstract base model

  • Pro: Single table for each category, faster queries

  • Con: separate relations need to be maintained for each category, no grouping possible at the db level.

    Adding a category always entails a migration!

4. Put all the category specific fields in Job (and make them nullable)

  • Pro: One Table, easy relations, Queries for special categories via filter on category field still possible.

    You can use specific model managers to handle categories: Job.tutoring.all()

    Possibly many categories share various subsets of fields

    No overengineering, easy maintainability.

    Adding a new category will only require a migration if it requires a field that is not there yet. You could have a generic CharField used by multiple categories for different semantic purposes and access it via propertys with meaningful names. These cannot, however, be used in filters or qs-updates.

À la:

class Job(models.Model):
    # ...
    attribute = models.CharField(...)

    def _get_attribute(self):
        return self.attribute

    def _set_attribute(self, value):
        self.attribute = value

    # for shopping
    shop_name = property(_get_attribute, _set_attribute)

    # for babysitting
    family_name = property(_get_attribute, _set_attribute)

# then you can use
babysitting_job.family_name = 'Miller'
  • Con: Some fields are null for each job

While options 1-3 may better model the real world and make you feel good about the sophisticated model structure you have cooked up, I would not discard option 4 too quickly.

If the category fields are few and commonly shared between categories, this would be my way to go.

The optimal thing to do would be to use a OneToOneField. Before further explanation, I'll just use this example:

    from django.db import models

    class Menu(models.Model):
        name = models.CharField(max_length=30)


    class Item(models.Model):
        menu = models.OneToOneField(Menu)
        name = models.CharField(max_length=30)
        description = models.CharField(max_length=100)

Menu here could compare to your Job model. Once an item in the menu is chosen, the Menu model basically extends the chosen Item's fields. Item here can be compared to your Job category.

You can read more on this stuff here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM