Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

confused about django foreignkey, manytomanyfield, inlineformset_factories

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

like image 952
trubliphone Avatar asked Jan 15 '12 18:01

trubliphone


1 Answers

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()
...
like image 92
Alasdair Avatar answered Nov 11 '22 18:11

Alasdair