Django: How to create a model that contains a foreign key with CreateView?

Question:

If possible, I want to make a model that includes the foreign key with class-based CreateView. I know it can be done using a function.

models.py

class Pizza(models.Model):
    name = models.CharField(max_length=20)
    def __str__(self):
      return self.name
    def get_absolute_url(self):
      return "/pizzas"

class Topping(models.Model):
    pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
    name = models.CharField(max_length=20)
    def __str__(self):
      return self.name
    def get_absolute_url(self):
       return reverse("pizza", kwargs={"pizza_id": self.pizza.pk})

views.py

class NewTopping(CreateView):
    model = Topping
    template_name = "pizzas/new_topping.html"
    form_class = ToppingForm

urls.py

path("topping/<pk>/create",views.NewTopping.as_view(),name="new_topping")

forms.py

class ToppingForm(forms.ModelForm):
    class Meta:
        model = Topping
        fields = ['name']

template

 <form action="{% url 'new_topping' %}" method="post">
    {% csrf_token %}
    {{form.as_p}}
    <input type="submit" value="submit">
  </form>

I’ve tried to do it like this, but I get an integrity error when I try to save the new instance:

NOT NULL constraint failed: pizzas_topping.pizza_id

Asked By: Dusan Mijailovic

||

Answers:

From a logical point of view, I would suggest, that you change your models, because a pizza can contain several toppings and a topping can be on several pizzas. So I would suggest, that you add a ManyToManyField "toppings" to your Pizza model and delete the ForeignKeyField to the Pizza model in the Topping model (see code below). If you implement it with your models, you will maybe run into problems.

Each object in the Topping table is only connected with one Pizza object. So if you have for example tomato sauce as a Topping, you have to add multiple objects with the name ‘tomato sauce’, each connected to a different type of Pizza. If you have for example ‘Pizza Margherita’, ‘Pizza Funghi’ and ‘Pizza Prosciutto’ as Pizza instances, you should have three instances of ‘tomato sauce’ in your Topping table in the database, because each of the three Pizzas has tomato sauce on it.

Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship.

So, with a ManyToManyField, Django creates a table (which you don’t see as a Model representation) which stores – in your case and with my suggestion – the ID of the Pizza and the ID of the Topping. For further explanation on ManyToManyFields, check the Django Documentation

#models.py
class Pizza(models.Model):
    name = models.CharField(max_length=20)
    toppings = models.ManyToManyField(Topping)
    ...

class Topping(models.Model):
    name = models.CharField(max_length=20)
    ...

Regarding your question:

You don’t include the ForeignKeyField inside the forms.py file you created. Change your forms.py file as follows:

class ToppingForm(forms.ModelForm):
class Meta:
    model = Topping
    fields = '__all__'

Now you should see a Dropdown or something else, where you can select the Pizza object you would like to add and then, this error shouldn’t appear. By default, all defined fields in a model aren’t nullable. If you want to make them nullable, you have to define null=True (Django Documentation)

Answered By: reez.zeer