Django models' best solution for this reverse ForeignKey - python

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

Related

Django Rest Framework - Way to make specific values within two columns in parent table a constraint to be only used together in child table as fk?

not sure I worded my question right in the title, let me try and explain better below...
Let's say a parent table called Share has two columns, a ShareCode Charfield(primary key) and a ShareName unique Charfield (string). This means we'll have a unique ShareCode and ShareName paired on each row of this table. Code for this below.
class Share(models.Model):
ShareCode = models.CharField(max_length=100, primary_key=True, default="N/A")
ShareName = models.CharField(max_length=100, unique=True, default="N/A")
Now let's say we have a child table, Fund. This table will consist of a FundID Autofield(primary key), a FundCode Charfield, and a FundName CharField. It will also have two foreign keys that reference the Share table, ShareCode and Sharename. Finally, it will have two unique_together constraints, FundCode and ShareCode as the first constraint, and FundCode and ShareName as the second. Code below.
class Fund(models.Model):
FundID = models.AutoField(primary_key=True)
FundCode = models.CharField(max_length=100, default="N/A")
FundName = models.CharField(max_length=100, default="N/A")
ShareCode = models.ForeignKey(Share, to_field='ShareCode', related_name='fundsShareCode',
on_delete=models.CASCADE)
ShareName = models.ForeignKey(Share, to_field='ShareName', related_name='fundsShareName',
on_delete=models.CASCADE)
class Meta:
unique_together = ('FundCode', 'ShareCode'),
unique_together = ('FundCode', 'ShareName')
Couple of things to note at this point, let's imagine that every share that we could possibly need is already in the Share table. The fund table's data however is being manually input by someone before being added to the database. The ShareName fk column has thus been added to the Fund table to make it easier to interpret, even if the ShareCode and FundCode columns would have sufficed for functionality by themselves.
Onto the problem now though. Let's say the person entering data into this table is adding multiple shares to a particular fund. So the FundCode 'FND123ABC' is spread across 3 rows, with ShareCode 'SHR1', 'SHR2', and SHR3' being the other half of the first unique_together constraint for those 3 rows.
They also need to satisfy the other constraint though. 'FND123ABC' is already provided for the second constraint, but the other half, ShareName, is being input by the user. The 3 shares that are being input are 'AbcCompany' (assigned to SHR1 in Share table), 'DefCompany' (assigned to SHR2 in Share table), and 'EfgCompany' (assigned to SHR3 in Share table). The user inputs in the Fund table for this second constraint the following (imagine that the input below is proper syntax):
Row 1...
FundCode: "FND123ABC",
ShareCode: "SHR1",
ShareName: "AbcCompany"
Row 2...
FundCode: "FND123ABC",
ShareCode: "SHR3"
ShareName: "DefCompany"
Row 3...
FundCode: "FND123ABC",
ShareCode: "SHR2"
ShareName: "EfgCompany"
Looking at those inputs above, Row 1 is fine. ShareCode 'SHR1' is matched with ShareName 'AbcCompany' in the Share table, so there's nothing wrong or inconsistent there. Rows 2 and 3 are a problem though. Row 2's ShareCode, 'SHR3' is assigned to ShareName 'DefCompany', whereas in the Share table, 'SHR3' is associated with the ShareName 'EfgCompany'. The final row has the same problem, with 'SHR2' being assigned to 'EfgCompany', when in the Share table 'SHR2' is associated with 'DefCompany' instead.
So with this informaton in mind, is there a way to prevent the user accidentally making a mistake like this? Removal of the ShareName field in the Fund table is one option, but I want to keep it there so the Fund table is much easier to read rather than a hard to read assortment of fund and share codes. So if we keep it, is there a way, a constraint or something, that can make sure that if ShareCode 'SH2' is being used for example, then it MUST be paired with its Share table associated field, 'DefCompany'?
Thank you for reading to the end, and I appreciate your assistance here.

Django filter 'first' lookup for many-to-many relationships

Let's say we have a two models like these:
Artist(models.Model):
name = models.CharField(max_length=50)
Track(models.Model):
title = models.CharField(max_length=50)
artist = models.ForeignKey(Artist, related_name='tracks')
How can I filter this relationship to get the first foreign record?
So I've tried something like this, but it didn't work (as expected)
artists = Artist.objects.filter(tracks__first__title=<some-title>)
artists = Artist.objects.filter(tracks[0]__title=<some-title>)
Is there any way to make this work?
Here's a solution not taking performance into consideration.
Artist.objects.filter(tracks__in=[a.tracks.first() for a in Artist.objects.all()], tracks__title=<some_title>)
No list approach, as requested.
Artist.objects.filter(tracks__in=Track.objects.all().distinct('artist').order_by('artist', 'id'), tracks__title=<some_title>)
The order_by 'id' is important to make sure distinct gets the first track based on insertion. The order_by 'artist' is a requirement for sorting distinct queries. Read about it here: https://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-DISTINCT

Django Queryset compare two different models with multiple rows

I have these two models that I would like to return the sum of. I get an database error about the subquery returning more than one row. What would be the best way to compare both without using a for statement?
AuthorizationT(models.Model)
ar_id = models.BigIntegerField(blank=True, null=True)
status_flag = models.BigIntegerField(blank=True, null=True)
BillT(models.Model)
paid_id = models.BigIntegerField(blank=True, null=True)
recvd = models.FloatField(blank=True, null=True)
Query I tried
paidbill= BillT.objects.values_list('paid_id', flat=true)
AuthorizationT.objects.values().filter(ar_id=paidbill, status_flag=0).aggregate(Sum('recvd'))
In SQL I know it would be
select sum(recvd) from authorization_t a, bill_t b where a.ar_billid0= b.paid_id and a.status_flag=0
I'm looking for the equivalent in queryset
I think you won't be able to achieve without a for loop because I think you need to join the tables as there is a filtration on both tables and you want to sum a field from the first table. The way to join tables would be prefetch_related() or select_related() but they utilize foreign keys.
This leads me to a suggestion that the id fields: bill_id and ar_id should be normalized as it looks like there will be data duplication. Using a relationship would also make making queries simpler.
Since paidbill is a list, you have to use the __in suffix in your query:
AuthorizationT.objects.filter(ar_id__in=paidbill,status_flag=0).aggregate(Sum('recvd'))
If you model the relation of the models (ar_id, paid_id) via a ForeignKey or ManyToMany, you will be able to do this trivially in a single ORM statement

How to compare Specific value of two different tables in django?

I have two tables 'Contact' and other is "Subscriber".. I want to Compare Contact_id of both and want to show only those Contact_id which is present in Contact but not in Subscriber.These two tables are in two different Models.
Something like this should work:
Contact.objects.exclude(
id__in=Subscriber.objects.all()
).values_list('id', flat=True)
Note that these are actually two SQL queries. I'm sure there are ways to optimize it, but this will usually work fine.
Also, the values_list has nothing to do with selecting the objects, it just modifies "format" of what is returned (list of IDs instead of queryset of objects - but same database records in both cases).
If you are excluding by some field other then Subscriber.id (e.g: Subscriber.quasy_id):
Contact.objects.exclude(
id__in=Subscriber.objects.all().values_list('quasy_id', flat=True)
).values_list('id', flat=True)
Edit:
This answer assumes you don't have a relationship between your Contact and Subscriber models. If you do, then see #navit's answer, it is a better choice.
Edit 2:
That flat=True inside exclude is actually not needed.
I assume you have your model like this:
class Subscriber(models.Model):
contact = models.ForeignKey(Contact)
You can do what you want like this:
my_list = Subscriber.objects.filter(contact=None)
This retrieves Subscribers which don't have a Contact. Retrieveing a list of Contacts is straightforward.
If you want to compare value of fields in two different tables(which have connection with ForeignKey) you can use something like this:
I assume model is like below:
class Contact(models.Model):
name = models.TextField()
family = models.TextField()
class Subscriber(models.Model):
subscriber_name = models.ForeignKey(Contact, on_delete=models.CASCADE)
subscriber_family = models.TextField()
this would be the query:
query = Subscriber.objects.filter(subscriber_name =F(Contact__name))
return query

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.

Categories

Resources