Django creates an extra unwanted entry in many-2-many intermediate table - python

I have a many-2-many relationship between Flight and Passenger. When I try to assign a passenger to a flight object, Django seems to add an extra entry to the intermediate table.
Here are the models:
class Passenger(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
class Flight(models.Model):
time = models.DateTimeField('flight time')
destination = models.CharField(max_length=20)
passengers = models.ManyToManyField(
to=Passenger,
symmetrical=True,
related_name='flights',
blank=True,
)
Say the intermediate table looks like this, with passenger
Say flight_object is a Flight with ID=1, and passenger_object is a Passenger with ID=2, when I run flight_object.passengers.add(passenger_object) Django adds 2 entries to the intermediate table in the database. The table now looks like this:
Both entries with ID=1 and 2 should be there, but 3 is incorrect, and the flight_id foreign key is for a completely different flight!

That's because of symmetrical=True. You shouldn't create symmetrical ManyToMany relation to any model other than self.
According to docs, when using symmetrical=True, Django tries to insert the symmetrical record in the through table.

Related

Add columns in model in DJango based on if else condition

This is my Django code for my model
I want to have columns in the model based on the value of chart type enter column there`
class DashboardCreativeQuery(models.Model):
query_name = models.CharField(max_length=256, null=False, unique=True)
query = models.TextField( null=False)
chart_type = models.ForeignKey(DashboardCreativeCharts, blank=True, null=True, related_name='chart_type',
on_delete=models.CASCADE)
if chart_type:
test= JSONField(null=False)
How can I do it?
By default, django, uses a Relational Database. A Relational Database store data in relations:
A relation is defined as a set of tuples that have the same attributes.
That means, in a relation (table) all tulles (rows) must have the same attributes (columns). For this reason, if you are using a relation (a table) to store your data, you should don't change model fields dynamically.
What can you do
Take a look to django model inheritance, maybe it is a solution for you.
Move your solution to a no-sql backend like mongo.

What's the use of Intermediate models in Django?

Why do we use an intermediate model?
Can't we just use Many to many relationship without intermediate model?
M2M relationships require intermediate tables. You can read more about what M2M relationships are and why they require an intermediate table (referred to as a junction table in the article) here:
Django abstracts this away by automagically creating this intermediate table for you, unless you need to add custom fields on it. If you do, then you can define it by overriding the through parameter as shown here
Here's a quick picture of why the table is required
Source: https://www.geeksforgeeks.org/intermediate-fields-in-django-python/
Let's say you have two models which have a Many-to-Many relationship, like Customer and Product. One customer can buy many products and a product can be bought by many customers.
But you can have some data that doesn't belong to neither of them, but are important to the transaction, like: quantity or date.
Quantity and date are the intermediary data which are stored in intermediary models.
from django.db import models
class Item(models.Model):
name = models.CharField(max_length = 128)
price = models.DecimalField(max_digits = 5, decimal_places = 2)
def __str__(self):
return self.name
class Customer(models.Model):
name = models.CharField(max_length = 128)
age = models.IntegerField()
items_purchased = models.ManyToManyField(Item, through = 'Purchase')
def __str__(self):
return self.name
class Purchase(models.Model):
item = models.ForeignKey(Item, on_delete = models.CASCADE)
customer = models.ForeignKey(Customer, on_delete = models.CASCADE)
date_purchased = models.DateField()
quantity_purchased = models.IntegerField()
When you buy a product, you do it through the Purchase model: the client customer buys quantity_purchased quantity of items item in date_purchased.
The Purchase model is the Intermediate model.
Django documentation says:
...if you want to manually specify the intermediary table, you can use
the through option to specify the Django model that represents the
intermediate table that you want to use.
In this case we have this line in the Customer model, which defines the intermediary model in through = 'Purchase'
items_purchased = models.ManyToManyField(Item, through = 'Purchase')
Let's now use the example from the Django Documentation.
You have a database of musicians with a Many-to-Many relationship with the bands the belong to: a musician can belong can be part of many bands, and the bands can have many musicians.
What data do you want to keep?
For musicians (person): name and instrument they play
For the bands: name and style.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
class Group(models.Model):
name = models.CharField(max_length=128)
style = models.CharField(max_length=128)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
But, wouldn't you think that knowing when the person joined the band is important? What model would be the natural place to add a date_joined field? It makes no sense to add it to Person or Group, because it's not an intrinsic field for each of them, but it's related to an action: joining the band.
So you make a small, but important adjustment. You create an intermediate model that will relate the Person, the Group with the Membership status (which includes the date_joined).
The new version is like this:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
class Group(models.Model):
name = models.CharField(max_length=128)
style = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
The changes are:
You added a new class called Membership which reflects the membership status.
In the Group model you added members = models.ManyToManyField(Person, through='Membership'). With this you relate Person and Group with Membership, thanks to through.
Something important to clarify.
An intermediate model, or in relational database terms, an associative entity, are always needed in a Many-to-Many (M2M) relationship.
A relational database requires the implementation of a base relation
(or base table) to resolve many-to-many relationships. A base relation
representing this kind of entity is called, informally, an associative
table... that can contain references to columns from the same or different database tables within the same database.
An associative (or junction) table maps two or more tables together by
referencing the primary keys of each data table. In effect, it
contains a number of foreign keys, each in a many-to-one relationship
from the junction table to the individual data tables. The PK of the
associative table is typically composed of the FK columns themselves. (source)
Django will create the intermediate model, even when you don't explicitly define it with through.
Behind the scenes, Django creates an intermediary join table to
represent the many-to-many relationship. By default, this table name
is generated using the name of the many-to-many field and the name of
the table for the model that contains it.
Django will automatically generate a table to manage many-to-many
relationships. However, if you want to manually specify the
intermediary table, you can use the through option to specify the
Django model that represents the intermediate table that you want to
use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.(source)

Flexible database models for users to define extra columns to database tables in Django

I am trying to build a tool that, at a simple level, tries to analyse how to buy a flat. DB = POSTGRES
So the model basically is:
class Property(models.Model):
address = CharField(max_length = 200)
price = IntegerField()
user = ForeignKey(User) # user who entered the property in the database
#..
#..
# some more fields that are common across all flats
#However, users might have their own way of analysing
# one user might want to put
estimated_price = IntegerField() # his own estimate of the price, different from the zoopla or rightmove listing price
time_to_purchase = IntegerField() # his own estimate on how long it will take to purchase
# another user might want to put other fields
# might be his purchase process requires sorting or filtering based on these two fields
number_of_bedrooms = IntegerField()
previous_owner_name = CharField()
How do I give such flexiblity to users? They should be able to sort , filter and query their own rows (in the Property table) by these custom fields. The only option I can think of now is the JSONField Postgres field
Any advice? I am surprised this is not solved readily in Django - I am sure lots of other people would have come across this problem already
Thanks
Edit: As the comments point out. JSON field is a better idea in this case.
Simple. Use Relations.
Create a model called attributes.
It will have a foreign key to a Property, a name field and a value field.
Something like,
class Attribute(models.Model):
property = models.ForiegnKey(Property)
name = models.CharField(max_length=50)
value = models.CharField(max_length=150)
Create an object each for all custom attributes of a property.
When using database queries use select_related of prefetch_related for faster response, less db operations.

Django point ForeignKey to an existing relationship table

I am reasonably new to Django and I want to achieve the following: I have a relationship between two tables, say table B has a ManyToMany reference to table A. Now I want a table called Options which saves options to a specific combination between A & B. How do I achieve this?
Thanks!
Hidde
Use the through option of the ManyToMany Field, and add the information in the relationship itself.
For example
class Ingredient(models.Model):
name = models.TextField()
class Recipe(models.Model):
name = models.TextField()
ingredients = models.ManyToManyField(Ingredient, through='RecipePart')
class RecipePart(models.Model)
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
amount = models.IntegerField()
# ...
RecipePart(recipe=pizza, ingredient=cheese, amount=9001).save()
If the relationship already exists (and you already have data) you will have to update the database schema (and create the model if you used to automatic mapping). South can help you do this.

Filtering object from one model accordingly to the field of another

I am making a new section on a website where existing customers (Customer model) can choose to appear on.
New users are not required to have an account from the main site (Customer) and can just create an account for the new section (NewSecUser model)
class Customer(models.Model):
name = models.CharField(max_length=50)
#[...]
is_visible_on_new_section = models.BooleanField(default=False)
class NewSecUser(model.Model):
name = models.CharField(max_length=50)
#[...]
customer_id = models.IntegerField(null=True)
# customer_id refers to the id of a Customer model object
# its value is different from null only when a Customer chooses to appear
# on the new section
How would one use exclude() to filter-out NewSecUser objects where Customer objects have an id equal to NewSecUser.customer_id and is_visible_on_new_section set to False?
Basically something similar to an SQL JOIN (with new_sec_user.customer_id=customer.id) I believe.
I know it would have been much easier with customer_id being a foreign key but I did not choose this.
Customer.filter(id__in = [nsu.pk for nsu in NewSecUser.all()]).filter(is_visible_on_new_selection=True).all()`
or something very similar

Categories

Resources