Django many-to-many query matching all related fields - python

I have two models connected by a many-to-many relationship in django.
class BuildingMapping(models.Model):
name = models.CharField(max_length=1000, null=False)
buildings = models.ManyToManyField(
Building, related_name="mapping"
)
class Building(models.Model):
function = models.CharField(
max_length=1000, blank=True, null=True
)
Function is a string containing one or more identifier divieded by a "/" e. g. "1300", "1300/2200", "1300/2230/7500", ...
I now want to perform a query that gets only BuildingMapping instances where the function for all Buildings is identical. I tried the following, but this will also return BuildingMapping instances where only one Building has "1300" as function.
BuildingMapping.objects.filter(buildings__function="1300")
Thanks!

If I understood correct, you want to get function field contains "1300". If it is right:
BuildingMapping.objects.filter(buildings__function__contains="1300")

I see that you are trying to fetch all the results which are matching 1300. What you are doing is exact match
strong text
You need to use icontains(case insensitive) or contains(case sensitive) for getting all matched data

Related

Django models' best solution for this reverse ForeignKey

I'm making a personal project to manage restaurants.
Two of my models are facing a problem, these models are DiningRoom and Table.
DiningRoom is the representation of any area that the restaurant could have (e.g. we could have one area inside and other area in the terrace of the building).
And in every DiningRoom we can set a layout of Tables.
So, the more object-oriented way I find to map this is by many-to-one relationship (ForeignKey). Since one DiningRoom can have many Tables, and one Table can be only in one DiningRoom. Right?
So my models are:
class DiningRoom(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE, null=False)
name = models.CharField(max_length=50, null=False, blank=False)
rows = models.IntegerField(max=15, null=False)
cols = models.IntegerField(max=15, null=False) # rows and columns are for the room's grid layout.
class Table(models.Model):
row = models.IntegerField(max=15, null=False) # The row in the room's grid where the table is at.
col = models.IntegerField(max=15, null=False) # the column in the room's grid where the table is at.
dining_room = models.ForeignKey(DiningRoom, on_delete=models.CASCADE, null=False) # Here is the problem.
The problem is that when I am querying DiningRooms of the account in the view, I need to fetch also the Tables that are related to each DiningRoom in the queryset result.
def dining_rooms(request):
try:
account = Account.objects.get(id=request.session['account_id'])
except Account.DoesNotExists:
return response(request, "error.html", {'error': 'Account.DoesNotExists'})
dining_rooms = DiningRoom.objects.filter(account=account)
But also I need the Tables of the results in dining_rooms!
I found two possible solutions but none seem to be "correct" to me. One is to make a Many-to-many relationship and validate that any Table is only in one DiningRoom in the view. And the second and worse one could be fetching the Tables once for each DiningRoom obtained in the queryset (but imagine a restaurant with 5 or 6 different areas (DiningRooms), it would be needed to fetch the database six times for every time).
Doing it in vice-versa and fetching all the Tables and select_related DiningRooms is not possible since it's possible to have a DiningRoom with no Tables in it (and in this case we will have missing DiningRooms).
What could be the best way to handle this? Thanks!
You can use the related_name or relationships backwards, an acceptable solution would be to create a method in the DiningRoom model that is called associated_tables() and return using the related_name (modelname_set, in this case it would be table_set). It is the name of the lowercase child model followed by the suffix _set
class DiningRoom(models.Model):
#your fields
def associated_tables(self):
return self.table_set.all()
In addition, this video tutorial could clear your days and give you a better idea about reverse relationships:
https://youtu.be/7tAZdYRA8Sw

Filtering queryset by number of many-to-one relationships with a raw SQL query

I am creating a resources page, and I have the following models:
class Tag(models.Model):
title = models.CharField(max_length=50, blank=False, unique=True, error_messages={'unique':"THis tag already exists."})
class Link(models.Model):
title = models.CharField(max_length=100, blank=False)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE, null=False, blank=False)
Which have more fields but they are irrelevant to this matter.
The problem with this resources page was that Tags with no Link establishing relationship with them were still appearing, and I wanted only for tags with at least one Link under them to appear.
Now, I have managed to do this with this queryset filtering:
tags = Tag.objects.all().filter(link__isnull=False).order_by("title").distinct()
Which works alright in my views.py, and what I want is done already. Note that the "distinct" is necessary so that it doesn't return duplicate queries.
I have had very little experience with MySQL and only have built very simple applications with it and PHP. I have now idea how it would function under SQLite, nor would I even know how to this with MySQL.
Under my (very informal) logic, it would function something like this (please, forgive my killing the language):
SELECT * FROM Tags WHERE NUMBER_OF_RELATIONSHIPS IS GREATER THAN 0
My question is actually how could this be achieved through a Raw SQL query, be it under SQLite or MySQL?
You want to get all tags that have more than one relationship (i.e. Links), correct? If so, you can use this as your raw SQL:
SELECT *
FROM <Tag_TableName>
WHERE <Tag_PK_Field> IN (
-- Get Tag Ids with more than one entry in Link table
SELECT <Tag_FK_Field>
FROM <Link_TableName>
HAVING COUNT(*) >= 1 -- Only return rows that have multiple entries per same tag
GROUP BY <Tag_FK_Field>
)
If that doesn't work, you may need to wrap the inner SELECT in another SELECT in order to filter out "tags" with more than one "link" entry.
I think Django defaults PK-field names to "id", so your actual raw query is probably closer to this:
SELECT *
FROM "<proj_name>_tag"
WHERE "id" IN (
SELECT "id"
FROM "<proj_name>_link"
HAVING COUNT(*) >=1
GROUP BY "id"
)

onetoone database relation between two tables?

I have 4 tables already exist in system.
Account
Project
document
Catalog
There are many Accounts in the system and each Account many have many Projects, each project have many documents.
[Current system not handle same project name in a different Accounts]
catalog Table:
class catalog(models.Model):
catalog_name = models.TextField(blank=True, null=True)
documents = models.ManyToManyField(document, blank=True, null=True)
Current system map documents of any project in the Catalog table.
We create catalog_name by project_name and add documents in the document
So Project name can not be repeated.
Project Table:
class Project(models.Model):
FK_Account = models.ForeignKey(Account, blank=True, null=True)
Project_ID = models.TextField(blank=True, null=True, db_index=True)
Project_Name = models.TextField(blank=True, null=True, db_index=True)
document Table:
class document(models.Model):
uid = models.TextField(blank=True, null=True,db_index=True)
Code to get Project documents:
def getAllProjectDocument(self, project_obj, current_selected_option='All'):
""" Returns All Project Documents. """
docs = []
cat_obj_list = catalog.objects.filter(catalog_name = project_obj.Project_Name + "-user-catalog")
if cat_obj_list:
catobj = cat_obj_list[0]
if current_selected_option=="All":
docs = catobj.documents.filter(status=2)
else:
docs = catobj.documents.filter(status=2, publish_status=int(current_selected_option))
return docs
Note: status 2 means document is any on project.
There are 950000 documents and 300000 projects in the system. If I have to get project documents of the of all project then I have to query on the catalog table for each project i.e. 300000 time and again do filtering in according to search and then add query set objects to python list and pass to template after finishing for loop
So for loop is running for 300000 times then query on catalog for same count i.e. 300000 and filter again same count i.e. ``300000` . So apache memory is increases.
I come with following idea : This is migration of Database.
I am going to create table which have one to one mapping og project and document.
There are are 4 types of document- 1. Private, 2. Project, 3. Library, 4. Delete document.
class Project_Document(models.Model):
FK_Project = models.ForeignKey(Project, blank=True, null=True)
FK_Dcoument = models.ForeignKey(document, blank=True, null=True)
So is **onetoone mapping is correct or anyone have different way to do this?**
Your ProjectDocument table is really just a many-to-many relationship, exactly as you already have between Catalog and Document. I don't think adding another one would really help, especially as you would then have to ensure the two relationships are kept in sync.
Instead you should add the missing relationship directly between Catalog and Project; I can't tell from the code whether that should be a one-to-many (ForeignKey) or one-to-one, and if the former which direction it should go. But assuming each Project has multiple Catalogs, you would add a ForeignKey on Catalog pointing at Project; then you could get all the Documents for a Project with one query:
project_docs = Document.objects.filter(catalog__project=my_project)
Note also you have some big inefficiencies in your table; you seem to be using TextFields throughout, which are stored as blobs in the database, and therefore have a massive overhead. You should be using CharFields for things like IDs and names; TextFields are only for large blocks of text such as the body content of a document itself.

How can I order a queryset by many to many related objects value?

I have a queryset containing Book objects.
Book has a m2m relationship to Author.
I want to order the books that are in the queryset by the name of the first author (authors would be sorted alphabetically so the first one would be the one whose last name comes first in the alphabet).
I guess it would be something about aggregation but I cannot seem to find what would be the right solution.
I have tried
books_qs.annotate(first_author=Min('author__lastname')).order_by('first_author')
but of course it is not the right type so I get faced with ValueError: could not convert string to float: Folland.
How should I go about this?
edit: Django 1.4.2 + MySQL
Of course you'll get a ValueError because you're passing a string to Min whereas it takes float.
That is, if your Author model has, say, age = models.FloatField(), you can pass age as an argument to Min, like: Min('age') but you can't pass a string, in your case, lastname.
I don't exactly understand your question. But if you want to order anything in your Django models, I guess easiest way would be by doing this with class Meta
class Author(models.Model):
lastname = models.CharField(max_length=100)
# other fields ...
class Meta:
ordering = ['lastname']
class Book(models.Model):
author = models.ManyToManyField(Author)
book_name = models.CharField(max_length=100)
# other fields ...
class Meta:
ordering = ['book_name']
All the authors will be sorted alphabetically as per their last name and all the books will be sorted alphabetically as per their name.

Django ImageField issue

I have a similar model
Class Student(models.Model):
"""A simple class which holds the basic info
of a student."""
name = models.CharField(max_length=50)
age = models.PositiveIntegerField()
photo = models.ImageField(upload_to='foobar', blank=True, null=True)
As we can see photo field is optional. I wanted all the students who have their images saved in the college DB. For that i did this
>>> Student.objects.exclude(photo__name=None)
But i am getting this error :
FieldError: Join on field 'photo' not permitted.
So, How can i extract all those students having their photos?
Any sort of help regarding this would be appreciated.
Thanks in advance.
It doesn't work because field lookups only work on other models. Here, name is an attribute on the return value of your photo field.
Try this instead:
Student.objects.exclude(photo__isnull=True)
It is preferred to use isnull instead of comparing equality to None.
EDIT:
Jeff Ober's suggestion:
Student.objects.exclude(photo='')
He says filtering is performed on the actual values stored in DB. In a file field's case the path to the file.

Categories

Resources