confused about django foreignkey, manytomanyfield, inlineformset_factories - django

All,
I am missing something fundamental about the underlying model for Django's ForeingKeys vs ManyToManyFields.
Suppose I am building an application about cars. I might have the following classes:
class Car(models.Model):
carName = models.CharField()
class Manufacturer(models.Model):
manufacturerName = models.CharField()
class Wheel(models.Model):
radius = models.IntegerField()
So far so good. Now there are some relations between these classes. A car has a manufacturer and has (four) tire(s). Conceptually, there is a difference though. The manufacturer is related via "aggregation"; a manufacturer can be associated to multiple cars; deleting a Car instance should not cause that car's manufacturer to be deleted as well. The wheels are related via "composition"; every four wheels associated with a car are associated with that and only that car; delete the car and the wheels should be deleted as well.
So, intuitively, that means that I ought to do the following:
class Car(models.Model):
carName = models.CharField()
manufacturer = models.ManyToManyField("Manufacturer")
wheels = models.ForeignKey("Wheel")
Ultimately, I want to use inlineformset_factories so that users can fill in details about a car, its manufacturer and wheels all at the same time. Something like this:
class CarForm(ModelForm):
class Meta:
model = Car
class ManufacturerForm(ModelForm):
class Meta:
model = Manufacturer
class WheelForm(ModelForm):
class Meta:
model = Wheel
Manufacturer_formset = inlineformset_factory(Car,Manufacturer,formset=ManufacturerForm)
Wheel_formset = inlineformset_factory(Car,Wheel,formset=WheelForm)
But most of the documentation that I find suggests that the ForiegnKey should go from Wheel to Car. This seems backwards to me, since the Wheel_formset would then present the user with all of the fields for a Car ("carName") and not a Wheel ("radius").
Just the act of typing this question is making me confused. Can anybody shed some light on how I can build a form that has all of a car fields, and then all of a manufacturer fields, and then all of a wheel fields.
Thanks

If each car has one manufacturer, then you should use a foreign key from Car to Manufacturer. This will allow multiple cars to have the same manufacturer, and manufacturers will not be deleted when cars are deleted. A many to many field suggests that one car can have multiple manufacturers.
Wheel should have a foreign key to Car. This will allow multiple wheels to have the same car, and the default Django behaviour when a car is deleted will be to delete the wheels.
So your models should look something like this:
class Manufacturer(models.Model):
name = models.CharField()
class Car(models.Model):
name = models.CharField()
manufacturer = models.ForeignKey("Manufacturer")
class Wheel(models.Model):
radius = models.IntegerField()
car = models.ForeignKey("Car")
For your view, I would first try to write views for the forms and formsets individually, and make sure you understand the relationships between your models before you bring them all together in one view.
This Stack Overflow question explains how to use a form and inline formset together at the same time (equivalent to the Car and Wheel models in your case). For the manufacturer, you probably want to exclude the manufacturer field from your CarForm, then set it in your view before you save.
...
manufacturer = ManufacturerForm.save()
car = CarForm.save(commit=False)
car.manufacturer = manufacturer
car.save()
...

Related

How to link hierarhical data into another model in Django?

Consider the following hierarchical data. It can be 3 to 4 levels deep. I understand I can use existing packages like django-mptt or django-treebeard to create a tree data structure.
Mercedes
Consumer
ModelA
ModelB
SUV
ModelC
Luxury
ModelD
Nissan
Consumer
ModelA
ModelB
SUV
ModelC
Now let's say I have another model called Battery. A battery can be compatible with multiple models for different market segments by different car vendors. So what I want to do is assign this battery to one or more compatible models above. I'm not sure how to accomplish this linkage in Django. Would it just be a ManytoMany field to the hierarchical model? Some pseudo-code would help.
class Battery(models.Model)
name = Charfield(max_length=50)
compatible_models = ????
I would also like to know how a query would be written. For example, I want to query all the battery that are compatible with ModelA by Mercedes, etc.
I think the below would work for you (in pure Django):
class CarBrand(models.Model):
brand_name = models.CharField(...)
class CarType(models.Model):
type_name = models.CharField(...)
available_brands = models.ManyToManyField(CarBrand)
class ModelType(models.Model):
model_name = models.CharField(...)
available_car_types = models.ManyToManyField(CarType)
class BatteryType(models.Model):
battery_name = models.CharField(...)
supported_models = models.ManyToManyField(ModelType)
You would query Batteries as follows:
BatteryType.objects.filter(
supported_models__model_name='ModelA',supported_models__available_car_types__available_brands__brand_name='Mercedes'
)

How to expand application to work with multiple users and seasons

Let's say that I have created an app for a school with several models (e.g. Student, Teacher, Course, Attendance, Grade, Timetable, Payments etc).
It's working great for the current year. But now I want to expand my application, so that several schools can use it and it can store data of (mostly) independent seasons/years.
The first solution that comes to my mind, is to add 2 extra models (1)school=user and (2)season=year. And then add ForeignKeys from (almost) ALL my models to both of these (school, season).
(Maybe I could add a third model named SchoolSeason, with just these 2 fields and use this as FK to all my fields.)
Is there a more elegant solution?
Edit: a drawback to this solution would be that the models (e.g. Students) will share their auto-incremented ID with other schools.
Hard to tell without your current models but I would to the same by adding to 2 extra models. But no need to add both of them to all of your models : only Season is needed if you link Season to School.
class School(models.Model):
name = models.CharField(...)
class Season(models.Model):
school = models.ForeignKey(School)
name = models.CharField(...)
other_fields...
Each Season is linked to a School. Then, you can add the Foreign Key to all of your models.
About your problem in the edit, you are right, it would be a problem. You should not use auto-incremented key but UUid.
Finally, it would look like :
class BaseSeasonModel(models.Model):
uid = models.UUIDField(
primary_key=True,
default=uuid_lib.uuid4,
editable=False,
)
season = models.ForeignKey(Season)
class Meta:
abstract = True
And all of your models would inherit from it :
class Student(BaseSeasonModel):
...

How to implement element ordering accross two different models in Django?

I've got these Car and Bike models I can't modify.
They may need to be linked to a Ferry model which I can modify. I want to implement ordering of these elements of two different models in the database and I want to avoid using Generic Foreign Keys.
So far this is what I've come up with:
class Car(models.Model):
pass
class Bike(models.Model):
pass
class Ferry(models.Model):
pass
class Lot(models.Model):
position = SmallInteger()
car = models.ForeignKey(to=Car, null=True, related_name="ferries")
bike = models.ForeignKey(to=Bike, null=True, related_name="ferries")
ferry = models.ForeignKey(to=Ferry, null=False, related_name="load")
Now my goal is to be able to access directly all the elements of a particular Ferry – be they cars or bikes – ordered by position, and all the ferries (in the context of ferry travel bookings there may be several) of a particular car or bike:
some_ferry.load.all().order_by("position")
some_car.ferries.all()
How do I create these relations, including a sort of combination of (Car + Bike)?
So you can put ordering on the table or as you make a query and you can use django's __ notation to use fields on related objects (more here)
For example;
class Car(models.Model):
class Meta:
"""
Metadata
"""
ordering = ('ferries__position', )
class Bike(models.Model):
class Meta:
"""
Metadata
"""
ordering = ('ferries__position', )
class Ferry(models.Model):
class Meta:
"""
Metadata
"""
ordering = ('load__position', )
class Lot(models.Model):
position = SmallInteger()
car = models.ForeignKey(to=Car, null=True, related_name="ferries")
bike = models.ForeignKey(to=Bike, null=True, related_name="ferries")
ferry = models.ForeignKey(to=Ferry, null=False, related_name="load")
class Meta:
"""
Metadata
"""
ordering = ('position', )
Or in your queries it might be Bike.objects.all().order_by('ferries__position')
If you define the ordering in the model meta, then that'll be the default ordering for all queries on that model so you don't then have to worry about it when you're making queries or looking at admin etc.
I don't see a way you can achieve what you want without using Generic Foreign Key, since the relationship you envision does not exist in Django (and, actually, is not a nicely normalized relational database pattern. Other database paradigms, such as OODB and GraphDB do include this pattern).
What you can do is to define a method on class Ferry that would query for all instances of Cars and Bikes and join them in a single resultset.
Another option is to use library django-model-utils, which would allow you to define a model, let's say, Transportation, that would act as some sort of virtual model, and you can define models Bike and Car as subclasses of it. With this solution, you can create a foreign key on Lot pointing to Transportation, and you can query for Bike, Car, or the generic Transportation instances.

Django model inherit from one of several models

I am new in Django an have trouble figuring out the right way of making model inheritance.
Let assume that I am making some kind of food app. I would then have a model for meet, a model for fruit, a model for vegetables and so on.
My question is: How can I make a nutrition content model, which can inherit from all of the above models but only from one at a time? For instance nutrition content of apple should only inherit from the fruit model.
If there was only one food model type I would use ForeignKey to handle the inheritance. I guess this is not an option when there are several options for models to inherit from.
I would like to use on_delete=models.CASCADE for the nutrition content model. Hence the request for inheritance.
Any suggestions will be appreciated.
Python class inheritance and Django model ForeignKey relationships are two completely different things. It sounds like you're referring to the latter.
Are the fields so different between the different food types that you actually need a different model for each one? The simplest way would be to just have single Food model for all food types.
class Food(models.model):
VEGETABLE = 'vegetable'
MEAT = 'meat'
FRUIT = 'fruit'
TYPE_CHOICES = [
(VEGETABLE, 'vegetable'),
(MEAT, 'meat'),
(FRUIT, 'fruit'),
]
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
nutrition_content = models.OneToOneField('NutritionContent', on_delete=models.CASCADE)
# additional fields
class NutritionContent(models.Model):
# additional fields
If your fields are so different between food types that you need to have different models for each one, you can set up Food as a parent model that all child food type models have a OneToOneField relationship with. Then the NutritionContent model can still link with Food.
class Food(models.model):
nutrition_content = models.OneToOneField('NutritionContent', on_delete=models.CASCADE)
# fields that apply to all foods
class Vegetable(models.Model):
food = models.OneToOneField('Food', on_delete=models.CASCADE)
# additional fields that only apply to vegetables
class Meat(models.Model):
food = models.OneToOneField('Food', on_delete=models.CASCADE)
# additional fields that only apply to meat
class Fruit(models.Model):
food = models.OneToOneField('Food', on_delete=models.CASCADE)
# additional fields that only apply to fruit
class NutritionContent(models.Model):
# additional fields

User as a subclass in Django

I'm working on a Django project for a medical team and have to include a calendar app to manage patients' appointments. Generally, a calendar is composed of several events with one or several attendees who are all Users. Here, attendees can be both doctors and patients. For practical and security reasons, they are separated in two different models: auth.User for medical staff, and Patient for the patients. My idea was therefore to create an abstract class called People on top of User and Patient:
class Event(models.Model):
title = models.CharField(max_length=255)
start = models.DatetimeField()
end = models.DatetimeField()
#...
class Attendee(models.Model):
event = models.ForeignKey(Event)
people = models.ForeignKey(People)
attendance = models.CharField(choices = ("Yes","No","Unknown"))
# Abstract class
class People(models.Model):
name = models.CharField(max_length=100)
#...
class Meta:
abstract = True
# Patient is a subclass of People
class Patient(People):
date_of_birth = models.CharField(max_length=100)
#...
Question is : How could I now make Auth.User also a subclass of People so that User could also be considered as attendees? Is it even possible? Otherwise how would you manage this issue?
You might want to turn things around, and inherit the medical staff model from AbstractUser and from People, like so class Staff(AbstractUser, People). This way, Staff has both the People model attributes and methods, and it still has the django User features.