Standard fields in all Django models classes - django

What is the optimal way to implement standard fields that I would like all classes in Django models to have?
Option 1
Option 1 is to create the standard fields in a parent class and then have every class inherit that class. For instance:
Option 2
Option 2 is to create a class with the standard fields that has a composite relationship to the other classes. For instance:
Option 3
Option 3 is simply to duplicate these fields in each of the classes that wishes to use them (which violates the DRY principle of Django). For example:
Which is optimal for a notes field and created and last modified datetimes (and in the future also user id)? I clearly will wish to use these fields for auditing purposes, which means they will be displayed in Django Admin in some way. Keep this in mind when helping me determine which approach is optimal.

Your question suggests that you want to build an abstract class to use as a base class for your models to inherit from. There is some really great documentation here.
For example:
class CommonInfo(models.Model):
notes = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(blank=True, default=datetime.datetime.now)
last_modified_at = models.DateTimeField(blank=True, default=datetime.datetime.now)
class Meta:
abstract = True # This line is what makes the class an abstract class
def we_all_do_this(self):
pass
def we_all_do_this_too(self):
pass
class ChildA(CommonInfo): # Note that ChildA class inherits from the CommonInfo abstract base class
name = models.CharField(blank=True, null=True, max_length=100)
state = models.CharField(blank=True, null=True, max_length=100)
def only_i_do_this(self):
pass
class ChildB(CommonInfo): # Note that ChildB class inherits from the CommonInfo abstract base class
title = models.CharField(blank=True, null=True, max_length=100)
date = models.DateField(default=datetime.datetime.today)
def only_i_do_this(self):
pass

I don't think there's a right answer to this question but if it is compulsory fields I expect to implement across all models in a project, I would go with the parent class approach.
i.e., your option 1.

Related

Django: Use a different related_name than "%(class)s" on abstract base classes to account for irregular plurals

I have the following models:
from django.db import models
from foo.bar.models import Location
class AbstractShop(models.Model):
location = models.ForeignKey(Location, on_delete=models.CASCADE, related_name="%(class)s")
class Meta(self):
abstract = True
class Bakery(AbstractShop):
some_field = models.BooleanField("Some field", default=True)
class Meta:
verbose_name = "Bakery"
verbose_name_plural = "Bakeries"
class Supermarket(AbstractShop):
some_other_field = models.CharField("Some other field", max_length=20)
class Meta:
verbose_name = "Supermarket"
verbose_name_plural = "Supermarkets"
Now, Supermarket as well as Bakery inherit the location-ForeignKey from AbstractShop.
If I want to query the reverse relation to Bakery on the Location model, I would have to use bakerys (instead of the correct bakeries) as related_name - which I don't want as it's grammatically wrong and unintuitive.
So my questions are:
Is there any way to use the verbose_name_plural as related_name?
Is there any way at all to use any other related_name than "%(class)s" or "%(app_label)s or do I just have to implement the ForeignKey on the child classes?
If so, that would be somewhat annoying. Imagine you have a lot of shared ForeignKeys: You can move the ones with regular plurals in English to the abstract base class (ABC) (because for regular nouns the added "s" in "%(class)s" results in the correct plural form) while those with irregular plurals have to be implemented on the child class (as only there the related_name can be set to the plural of the actual name of the child class, which you don't know in the ABC).
This is a totally arbitrary condition which might not be obvious to non-native English speakers, also it transfers linguistic logic to code, which IMHO shouldn't happen.

How do I reuse single model field validators for forms without using ModelForm?

How can I reuse model field validators when creating a form. I can't use ModelForm because the form only uses part of the model's fields and I also have additional form fields.
Minimal example
I have a model for an (encryption) key that is always 32 characters in length and I want to use this restriction in the model and forms that accept that key.
models.py
class EncryptionKey(models.Model)
key = models.CharField("Encryption Key", max_length=32, validators=[validators.MinLengthValidator(32)])
forms.py
class NewUserForm(forms.Form):
first_name = forms.CharField(label='First Name', required=True, max_length=256)
last_name = forms.CharField(label='Last Name', required=True, max_length=256)
key = # How do I reuse the key from the model here?
I am looking for a idiomatic way to do this in Django 2.1.
I spend >20 minutes googling for this but all I find is how to use ModelForm.
Not sure if it is idiomatic or not but you can use fields_for_model helper from django.forms.models:
from django.forms.models import fields_for_model
from yourapp.models import EncryptionKey
class NewUserForm(forms.Form):
first_name = forms.CharField(label='First Name', required=True, max_length=256)
last_name = forms.CharField(label='Last Name', required=True, max_length=256)
key = fields_for_model(EncryptionKey, ['key'])['key']
I am not even sure if that is documented.
I think I found the "canonical" answer in the Django docs for Models (emphasis mine):
Abstract base classes
Abstract base classes are useful when you want to put some common
information into a number of other models. You write your base class
and put abstract=True in the Meta class. This model will then not be
used to create any database table. Instead, when it is used as a base
class for other models, its fields will be added to those of the child
class.
An example:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
The Student model will have three fields: name, age and home_group.
The CommonInfo model cannot be used as a normal Django model, since it
is an abstract base class. It does not generate a database table or
have a manager, and cannot be instantiated or saved directly.
Fields inherited from abstract base classes can be overridden with
another field or value, or be removed with None.
For many uses, this type of model inheritance will be exactly what you
want. It provides a way to factor out common information at the Python
level, while still only creating one database table per child model at
the database level.
The only downside I can see with this, is that inheritance is used for "has-a" relations and not "is-a". Following the above example: A Student is modelled as being a specialization of common info but obviously isn't.

Django model inheritance creating or get from parent

Am trying to build a project and it seems that using model inheritance for my requirements can work the best, I have one base model in which I have several model uses it for inheritance. In the base model, I have a slug field which is unique across all (for integrity)
In my child model, sometimes I want to create a record but if a parent already exists, I want to create only the child and link to the parent directly. for example,
class Base(models.Model):
slug = models.SlugField(_('Slug'), unique=True)
#Other fiels
class ChildA(Base):
height = models.CharField(max_length=100, )
class ChildB(Base):
is_for_sale = models.BooleanField(_('Is active'), default=True, )
# when creating ChildA, it will automtically insert into base model as well
ChildA.objects.create(slug='bmw', height='11')
# now for childB, I want the newly created object to link to an existing record in Base where slug
# is the unique value, is it possible todo such a thing?
ChildB.objects.create(slug='bmw', is_for_sale=True)
I think you might have misunderstood inheritance at this point.
If I get it right, you say that slug must be unique. Therefore you restrict the amount of Classes with a certain slug to '1'
Your Classes ChildB and ChildA are not "linked" to BaseClass, but are descendants of BaseClass, therefore an instance of a child is also an instance of BaseClass.
To me it seems you a looking for a relation between a Slug Object and multiple other Objects with different properties.
Instead of trying to derive from Class Base I would suggest a relation like:
class ChildA(models.Model):
height = models.CharField(max_length=100, )
slug = ForeignKey(Base)
This will ensure that the right Slug Object will be used and not created again.
If you need to keep that line of inheritance, keep your base class, but extract the slug field into its own class like
class Slug(models.Model):
slug = models.SlugField(_('Slug'), unique=True)
class Base(models.Model):
# all your other fields
slug = ForeignKey(Slug)
class ChildA(Base):
height = models.CharField(max_length=100, )
class ChildB(Base):
is_for_sale = models.BooleanField(_('Is active'), default=True, )
Once you are here you can use the constructor of these classes to enforce further restrictions on Slug if they are not yet provided
If you just want a set of common attributes, you want to make your base model abstract, otherwise django will create a table for it.
If instead you are interested in a relation among all subclasses of Base, then you have to give up on inheritance and use a foreign key from every child to Base:
class ChildA(models.Model):
base = ForeignKey(Base)
height = models.CharField(max_length=100)

How to architect and implement models with similar state and behavior in django?

I have the following models:
class Member(models.Model):
name = models.CharField(max_length=255)
class Location(models.Model):
name = models.CharField(max_length=255)
member = models.ForeignKey(Member)
class Department(models.Model):
name = models.CharField(max_length=255)
member = models.ForeignKey(Member)
class LocationInvite(models.Model):
sent = models.BooleanField(default=False)
location = models.ForeignKey(Location)
def send(self):
location = self.location.member
email = self.location.member.email
send_populated_email(location, email)
self.sent = True
I need to allow departments to have invites as well.
I am thinking of changing LocationInvite to Invite and making it an abstract base class. I will then create 2 concrete implementations LocationInvite and DepartmentInvite. I will move the location foreign key to the LocationInvite class.
How then will I refactor the send method of Invite to accommodate extracting the email address of either the Location or the Department, depending on the concrete implementation?
My question is, is using an abstract base class a good architecture move and how would I implement it given the constraints of the send method?
These records will be in the millions, so that is why I did not mention using generic foreign keys. Unless this is not a problem?
Refactor the attribute access into a separate property, and override in each model.
class Invite(...):
def send(...):
self._group....
...
...
class LocationInvite(Invite):
...
#property _group(self):
return self.location
class DepartmentInvite(Invite):
...
#property _group(self):
return self.department
I created an abstract base class and let both invite types extend from it.

Model relationships in Django

I am new to Django and databases and after reading the Django documentation on models I have the following question:
Let's say I have 3 models: VehicleName, CarManufacturer and TruckManufacturer. I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames. What is the relationship here and how to define it in Django? Is it as simple as define a models.ForeignKey(VehicleName) in both CarManufacturer and TruckManufacturer?
Thanks.
from django.db import models
class CarManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class TruckManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class VehicleName(models.Model):
# ...
To do exactly what you're describing:
I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames
You'd create a nullable foreign key on VehicleName to both of your Manufacturer models:
class CarManufacturer(models.Model):
# field definitions here
class TruckManufacturer(models.Model):
# field definitions here
class VehicleName(models.Model):
car_manufacturer = models.ForeignKey(CarManufacturer, blank=True, null=True)
truck_manufacturer = models.ForeignKey(TruckManufacturer, blank=True, null=True)
Then, instances of CarManufacturer or TruckManufacturer can get the names via the vehiclename_set attribute.
For a more advanced design, I would probably try to abstract the shared manufacturer behavior into a single model, then use multi-table inheritance:
class Manufacturer(models.Model):
# shared car and truck manufacturer fields go here
class CarManufacturer(Manufacturer):
# car manufacturer specific fields go here
class TruckManufacturer(Manufacturer):
# truck manufacturer specific fields go here
class VehicleName(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
See the multi-table inheritance docs for full details.
I do not think you are understanding the manufacturer to vehicle relationship property. What I think you are trying to show is that a certain Vehicle belongs to a certain manufacturer.
This type of relationship would actually be defined in the Vehicle class, as a foreign key, called manufacturer, in the Vehicle class.
In the case you are defining many vehicles under a manufacturer, you just need to rename the property to car_model or something of the like and you should be fine.
I think you have the understanding mapped out well enough. Just remember that foreign keys are only a property of one table, and say nothing about the other table itself until the relationship is established there also.
If you're working with a larger relationship, with multiple objects, you should look into using the Many-to-many field described in the django documentation.
They have an example that shows how an Articles have many Publications:
class Publication(models.Model):
title = models.CharField(max_length=30)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.headline
class Meta:
ordering = ('headline',)