Django ORM inner join with table sharing the same foreign key - django

I have three tables:
Table 1 : order
Fields: id, client_reference, price and status
(status is a foreignkey linked to order_status.id)
Table 2 : order_status
Fields: id, lastupdate
Table 3 : order_status_i18n
Fields: id, order_status_id, language and label
(order_status_id is also a foreignkey linked to order_status.id)
what I'd like to do is to get the order_status_i18n label instead of order_status_id based on the user language, so the SQL request would be like:
SELECT o.client_reference as reference, o_s_i18n.label
FROM
order AS o
INNER JOIN order_status_i18n AS o_s_i18n
ON o.status=o_s_i18n.order_status_id WHERE o_s_i18n.language='the language';
and the Model for order contains a foreignkey attribute linked to the order_status model but not order_status_i18n, so I tried to use
Order.objects.filter(some_filters).prefetch_related('status')
.filter(status__in=OrderStatusI18N.objects.filter(language='the_language'))
Which gives me a queryset conflict telling me that I should use the queryset for OrderStatus rather than OrderStatusI18N and I totally agree to this.
But anyway, what would be the good way to manage such a request using django ORM?

I think this should do the thing.
Order.objects.filter(some_filters).prefetch_related('status').annotate(i18_label=F('status__order_status_i18n__language')).filter(status__order_status_i18n__language='en')

Related

Django and Postgres inserting into many-to-many table not working

I'm using python and psycopg2 to scrape some data and insert them into my database.
I've already created the movies and actors tables inside my Django models.py and inside my movies table, there is a many to many relationship between movies and actors table.
below is my models.py:
class Movie(models.Model):
name = models.CharField(max_length=55)
summary = models.TextField(max_length=1024)
actor = models.ManyToManyField(Actor, blank=True)
when I create a movie from Django-admin I select which actors are included in the movie and everything works fine and all the related actors for that movie will show up on my website.
But the problem is that when I try to insert scraped data into my database outside of my Django project folder, the related actors won't be shown on my website because obviously, I have not set the many to many relationship between them.
I have tried creating a junction table using SQL commands which gets the movie id and the actor's id and links them together but I don't know how I should tell Django to use that table and show the related actors for each movie.
This is the SQL code I use to insert into my db:
INSERT INTO movies(name, summary)
VALUES ('movie name', 'sth')
and the code to insert to actors table:
INSERT INTO actors(name, bio)
VALUES ('actorname', 'sth')
Both actors and movies table have auto generated id and I insert them insto the junction table using the code below:
INSERT INTO movie_actors (actor_id, movie_id)
VALUES (
(SELECT actor_id from actors where name='actor name'),
(SELECT movie_id from movie where name='movie name')
)
Am I doing it right?
I would really appreciate it if someone could help me with this.
Django automatically creates a table for many2many relationships. From docs:
ManyToManyField.through
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.
So you must find the name of the table that django had already created.
Secondly, I suggest that you use django's ORM instead of raw queries so you don't have these kind of problems anymore.
Django automatically creates a through table for M2M relations, if you need you can specify custom through table. In your case I think there is no need of custom through table.
I using Django ORM instead of writing raw query.
INSERT INTO movies(name, summary) VALUES ('movie name', 'sth')
instead of tis raw query you can use the following ORM query:
movie = Movie.objects.create(name="movie name", summary="movie sammuary")
This will create a movie entry in the Movie table.
Next to create user entry you can use the following query:
actor = Actor.objects.create(name="actor name", bio="actor bio")
Now you created the entries in both the table, next you can establish the realtion, for that you have to use the following query:
movie.actor.add(actor)
Incase if you want to add multiple actors at the same time, you create multiple actors object and use following query:
movie.actor.add(actor1, actor2, actor2)
For more details you can check django's offical documentation

How to use Joined table in django queryset

i was join two table and i want to get field in joined model class
school_name_list = Students.objects.select_related('School').values('school_name')
but this code raise
django.core.exceptions.FieldError: Cannot resolve keyword 'school_name' into field``
how can i solve it?
If School is foreign key attribute in Students model, then use School__school_name in values.
students = Students.objects.values('School__school_name')
If you are accessing only a particular field like School__school_name only (using values), there is no need to add select_related because fields that are in values will be fetched.

How should a many-to-many table be defined

I'm having some trouble understanding many-to-many fields in Django.
When I create a many-to-many field, ex:
class GlobalPart (Models.model):
...
category_id=models.ManyToManyField(Category, related_name = 'globalpart')
...
and
class Category (Model.model):
...
category = models.CharField(max_length=250)
...
I notice that it created a new table called appname_globalpart_category_id in addition to the appname_globalpart table for the GlobalPart model.
What I'm wondering is, how should the field types in that table be defined. I would think that
there should be at least one foreign key there to relate the fields. But instead there is the primary key for the table, and the other fields are integers (globalpart_id and category_id).
So my question is -- is that normal? Or did I somehow define the many-to-many field incorrectly? And my next question is how would I get all the category_ids associated to a particular GlobalPart?
(1) short answer: Yes this is normal.
Long answer: ManyToMany table will need a foreign key to both Category and GlobalPart tables. Strictly speaking those two foreign keys should be sufficient. The extra pk that you see in there is just for django magic. You can really get away with only those two foreign keys in that table if you manually define the many-to-many table yourself. However if you let django do it for you (by using ManyToManyField) you get this extra pk
(2) I suggest changing your model fields category_id to categories:
class GlobalPart (Models.model):
categories=models.ManyToManyField(Category, related_name = 'globalpart')
This is because, ManyToManyFields refers well to "many" items. This field does not refer to "one" category_id, it refers to all related categories. So when naming it would be natural to name it accordingly.
As for accessing all categories you can do it by accessing the "categories" property. Say if your object instance named global_part, you can access categories like this:
categories = global_part.categories.all()
Instead of all(), you can use filter() or exclude() the same way you use it when querying models.
Here is a link to related django docs
What do you think a foreign key is? It's a field containing values that equate to IDs - usually primary keys - in the "foreign" table. If the other table has integer keys, as most Django tables do, then the foreign key field will be of type integer as well.
Additionally, Django creates constraints so that the database will enforce that the IDs do actually reference valid values in the foreign table. Depending on your database, these might or might not be displayed as part of the field definition.

Django. Many-To-Many Field for form, but not for model

I have DB that should have one field with type Many-To-Many, but it is not and I can't change this.
For example I have a list of students and a list of subjects. Subject should be many-to-many field in students table, but as i said it is not. Students table doesn't have this field at all. But there is still another table students-subjects that contains subject_id-student_id items.
How can I embed in student form some kind of subject field that could change students-subjects data when I save a student in DB? The problem is that I can't change the DB structure so need to make it only with help of Django.
There is no such thing as a 'Many-to-many field' for a database-table like you might know it of foreign keys. In database representation many-to-many relationships are realized by using an extra table that links the primary keys (usually the ids) of the records you want to set in relation. In your case that is the table students-subjects. Django uses this table when you define a many-to-many relationship in the model. You do not have to change your database structure at all, it will be working perfectly as it is now.
See the documentation: ManyToManyField
You'll have to set the db_table option with the name of your intermediary table (i.e. students-subjects). Then everything should work fine.
EDIT:
Considering your comment, the problem is that Django expects a certain naming convention (i.e. MODELNAME_id) which isn't provided by your table. Since you say that you cannot change the table itself, you have to try something else.
You have to create an extra Model for your intermediary table (students-subjects) and define the field 'students' as a foreign key to the students model and the field 'subjects' as a foreign key to the subjects model. Then for the many-to-many field you specifiy the option 'through' with the name of your intermediary table. Set the options 'db_column' to let Django know which names you'd like to use for the databse columns. 'db_table' in the meta class is needed to specify your database table name.
You get something like:
class StudentsSubjects(models.Model):
student = models.ForeignKey(Student, db_column='student')
subject = models.ForeignKey(Subject, db_column='subject')
class Meta:
db_table = 'students-subjects'
class Student(models.Model):
...
subjects = models.ManyToManyField(Subject, through='StudentsSubjects')
...
class Subject(models.Model):
...
I hope that will help you.
For more detail see: Extra fields on many-to-many relationship.

Django model with 2 foreign keys from the same table

I wanted a Django model with 2 foreign keys from the same table. It's an event table which has 2 columns for employees: the 'actor' and the 'receiver'. But I get this error:
Error: One or more models did not validate: tasks.task: Intermediary
model TaskEvent has more than one foreign key to Employee, which is
ambiguous and is not permitted.
Is there a better way to model this?
I think I'm going to add a TaskEvent_to_Employee table. There will be two records in it, one for each of the two employees related to each TaskEvent. Does anyone know an easier workaround?
I haven't done this yet, but I used inspectdb to generate the models.py file from an existing DB that does exactly that - this is what inspectdb threw back, so it should work:
creator = models.ForeignKey(Users, null=True, related_name='creator')
assignee = models.ForeignKey(Users, null=True, related_name='assignee')
Hope that works for you - if it doesn't I am going to have a problem too.
I think what you're looking for is the related_name property on ForeignKeyFields. This will allow you to reference the same table, but give django special names for the relationship.
More Info:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
https://docs.djangoproject.com/en/dev/topics/db/queries/#backwards-related-objects
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_one/
From the error message, it sounds like you're trying to put two foreign keys to the same object on an intermediary table used via the through argument to ManyToManyField, the documentation for which states:
When you set up the intermediary
model, you explicitly specify foreign
keys to the models that are involved
in the ManyToMany relation. This
explicit declaration defines how the
two models are related.
There are a few restrictions on the
intermediate model:
Your intermediate model must contain one - and only one - foreign key to
the target model (this would be Person
in our example). If you have more than
one foreign key, a validation error
will be raised.
Your intermediate model must contain one - and only one - foreign key to
the source model (this would be Group
in our example). If you have more than
one foreign key, a validation error
will be raised.
Using related_name was my solution:
class Sample(models.model):
...
class Mymodel(models.model):
example1 = models.ForeignKey(Sample, related_name='sample1')
example2 = models.ForeignKey(Sample, related_name='sample2')
The fact that two columns are part of one table implies that the two fields are related, therefor to reference them individually is not ideal. The ForeignKey of your model should be the primary key of the table you are referencing:
event = models.ForeignKey('event')
You would then reference the columns as such:
foo.event.actor
foo.event.receiver
If you wish you could also change the way your class/model references the foreign attributes with properties. In your class you would do the following:
#property
def actor(self):
return self.event.actor
#property
def receiver(self):
return self.event.receiver
This would allow you to then call foo.actor and foo.receiver but I believe the longer, foo.event.actor would be more pythonic