Combine the information of two tables into a single Django model - django

I need to read data for an external database (I cannot modify the table structure) with the two following tables:
Table1
Key (primary key)
Field1
Table2
Key (primary key)
Field2
Is it possible to combine these two tables into a single Django Model that looks like:
Model:
Key (primary key)
Field1
Field2
Additional information:
The keys of the entries in Table2 are a subset of the entries in Table1. So for any entry in Table2 there is a matching entry in Table1, but not viceversa
Work so far:
I have a one model for each table (1 and 2) and Table1 model has a "field2" #property that looks up the corresponding information in the Table2 model.
Also, I can do this at the sql level with the following query:
SELECT
table1.key,
table1.field1,
table2.field2,
FROM
table1
LEFT OUTER JOIN table2
ON table1.key=table2.key
ORDER BY table1.key ASC
===
Solution implementation details update after seeing some of the answers:
From Daniel Roseman's answer
I ended up with two models:
class Model1(models.Model):
key = models.CharField(max_length=100, primary_key=True)
field1 = models.TextField()
class Meta:
managed = False
db_table = 'Table1'
def field2_value(self):
try:
return self.field2.value
except Model2.DoesNotExist:
return None
class Model2(models.Model):
key = models.OneToOneField(Model1, primary_key=True, db_column='key',
related_name='field2')
field2 = models.TextField()
class Meta:
managed = False
db_table = 'Table2'
While it is not exactly what I had originally in mind when I asked this question, it meets my desired use case
Also, here are the corresponding admin classes:
class Model2Admin(admin.StackedInline):
model = Model2
#admin.register(Model1)
class Model1Admin(admin.ModelAdmin):
inlines = (Model2Admin,)
list_display = ('key', 'field1', 'field2_value')
# Loads the related property in one SQL call instead of one call per entry
list_select_related = ('field2',)
Thanks KaaN SARIKAYA for your great answer. I didn't know that I could create a view of the two tables. If I could modify the database structure I would opt for your option.

A better solution would be to keep these as two models, but have the primary key of the second be a OneToOneField to the first.

You cannot merge the model because they are 2 physical tables, django models are tied by the database table names so you couldn't do what you want. If you have the control for those external database tables and have the right to modify them, you should modify the schema first and merge the data. If you don't want to or you cannot modify anything, your current way is OK.

You can create a table_view on sql and make an abstract model on django . Suppose you have 2 pyhsical models and tables.
SQL CODE:
CREATE OR REPLACE VIEW combine_two_model_view AS
SELECT
mt1.name as mt1_name,
mt2.name as mt2_name,
mt1.pub_date as mt1_pub_date
.
.
.
.
FROM modeltable1 mt1
LEFT JOIN modeltable2 mt2 ON mt2.id = mt1.id
ALTER TABLE combine_two_model_view
OWNER TO dbuser;
we created sql view table. now, we will create an abstract model
in your models.py:
class YourModelsCombine(models.Model):
class Meta:
abstract = True
db_table = "combine_two_model_view"
mt1_name = models.CharField(max_length=100)
mt2_name = models.TextField(max_length=2000)
mt1_pub_date = models.DateTimeField("Publish Date", auto_now_add=True)
mt2_pub_date = models.DateField("Updated Date", auto_now=True)
mt1_integer = models.IntegerField(default=0)
#etc
attention: make sure your table view variable names and model variable names must be same. Finally;
in your admin.py:
admin.site.register(YourModelsCombine)
You will see combine of two tables on your django-admin
I hope this helps to you

Related

Django select_related the join table only for a many-to-many relation

I have a Django application with a model A with a ManyToManyField bees to model B:
from django.db import models
class A(models.Model):
bees = models.ManyToManyField("B", related_name="aas", blank=True)
field1 = models.TextField()
field2 = models.TextField()
class B(models.Model):
field1 = models.TextField()
field2 = models.TextField()
For one view, when I select a bunch of A's I also need the ids of their B's. The default where Django queries the related B's for each A individually is too slow, so I use select_related.
If I do A.objects.select_related('bees') Django selects the full B models:
SELECT ("app_a_bees"."from_a_id") AS "_prefetch_related_val_from_a_id",
"app_b"."id",
"app_b"."field1",
"app_b"."field2",
FROM "app_b"
INNER JOIN "app_a_bees" ON ("app_b"."id" = "app_a_bees"."to_b_id")
WHERE "app_a_bees"."from_a_id" IN (... list of A ids ...)
But I only need their id values, so I only need to select the app_a_bees join table to get them, not the B model table.
I tried A.objects.select_related('bees__id') (I also tries 'bees_id') but Django doesn't like that, individual fields cannot be prefetched in this way.
I have also tried A.objects.select_related(Prefetch("bees", queryset=B.objects.all().only("id")), but that still joins to the B table to select the id field, which Django already has from the join table.
Is there any way to prefetch just the join table for my A objects?

Django filter queryset of a models that are related with a foreign key to other model?

I have a simple question.
I have two models(Waiter and Manager) and they both contains same foreign key to restaurant as:
class Managers(BaseUser):
restaurant = models.ForeignKey(Restaurants, on_delete=models.CASCADE)
class Waiters(BaseUser):
restaurant = models.ForeignKey(Restaurants, on_delete=models.CASCADE)
And restaurant model:
class Restaurants(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(max_length=250)
location = models.CharField(max_length=200)
rating = models.DecimalField(null=True, decimal_places=2, max_digits=5)
So I need to get all waiters that restaurant=managers__restaurant_id.
I think in SQL would be:
select *
From Waiters w
Left outer join Managers m
On w.restaurant_id = m.restaurant_id
Note*
I'm able to query that like below:
manager = Managers.objects.filter(id=request.usr.id)
queryset=Waiters.objects.filter(restaurant=manager.restaurant_id)
But is there any way that i could do it in one query.
But is there any way that i could do it in one query.
This is in one query, but it will work with a subquery is probably not that efficient.
You can however filter in a more compact way with:
Waiters.objects.filter(restaurant__managers=request.user.id)
We can look "through" relations by using double underscores (__). Here we thus are looking for Waiters objects for which it restaurant is related to a Managers object with the given id of request.user.id.
how about this??
queryset = Managers.objects.filter(id=request.usr.id and Waiters.objects.filter(restaurant=manager.restaurant_id))

How are many-to-many relationship handled in Django?

I have a shirt which can contain multiple colors, and multiple colors which can have multiple shirts. Normally I would express it the following way:
In django I have the many-to-many (https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/)
Example:
publications = models.ManyToManyField(Publication)
--
Can I create the table "Item_colors" consisting of 2 columns (no "ID" primary key) and design the models according to my diagram using the composite key:
class Item_colors(models.Model):
class Meta:
unique_together = (('cloth_item_id', 'color_id'),)
cloth_item_id = models.ForeignKey(Cloth_item, on_delete=models.CASCADE)
color_id = models.ForeignKey(Color, on_delete=models.CASCADE)
How is the many-to-many relation handled in a DB context, and does it yield better performance?
EDIT: https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys no avoiding primary keys in favor of composite keys saving columns :( at least for now..
How is the many-to-many relation handled in a DB context, and does it yield better performance?
With a junction table in the middle, so with an item_colors table. But the table contains a primary key, as does every model in Django.
If you do not specify a through=… parameter [Django-doc] to define the model for the junction table yourself, Django will automatically create such model. This model then has two ForeignKeys to the two models it connects as discussed in the database representation section of the documentation:
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. Since some databases don’t support table names above a certain length, these table names will be automatically truncated and a uniqueness hash will be used, e.g. author_books_9cdf. You can manually provide the name of the join table using the db_table option.
But the table thus has a primary key. This might be useful if the same object occurs a second time in the relation.
You can access the through model in the Article-Publication example for example with:
Article.publications.through
You thus can define a through model yourself, for example with:
class Color(models.Model):
color = models.CharField(max_length=128)
class ClothItem(models.Model):
item_name = models.CharField(max_length=128)
colors = models.ManyToManyField(
Color,
related_name='cloth_items'
through='ClothItemColors'
)
class ClothItemColors(models.Model):
cloth_item = models.ForeignKey(ClothItem, on_delete=models.CASCADE)
color = models.ForeignKey(Color, on_delete=models.CASCADE)
class Meta:
db_table = 'item_colors'
constraints = [
models.UniqueConstraint(
fields=('cloth_item', 'color'),
name='unique_cloth_color'
)
]
often an explicit through model is used to store extra information, for example the quantity:
class ClothItemColors(models.Model):
cloth_item = models.ForeignKey(ClothItem, on_delete=models.CASCADE)
color = models.ForeignKey(Color, on_delete=models.CASCADE)
quantity = models.IntegerField(default=0)
# …

Django union two tables into single model

I have an external database which I can't modify in any way (read-only). It has three tables - Company (id), CompanyContact (company_id, contact_id), Contact (id, company_id).
Basically, Contact has a nullable foreign-key to Company table and it works as many-to-one, but if company_id is null, I have to look into CompanyContact table, which is many-to-many kind relationship.
How can I combine these two tables (Contact and CompanyContact) into one model - Contact? In other words, how can I get all contacts for a given company?
In SQL that would be something like:
select contact.id from contact where company_id = XXX
union
select contact_id from companycontact where company_id = XXX
Django models:
class Company(models.Model):
uuid = models.CharField(max_length=36, primary_key=True, db_column='id')
class Meta:
managed = False
class Contact(models.Model):
uuid = models.CharField(max_length=36, primary_key=True, db_column='id')
company = models.ForeignKey(Company, db_column='company_id')
class Meta:
managed = False
I don't have a model for CompanyContact. And there is nothing to show in views because that basically is my question, how to get contacts for a given company.
The original tables in your database are not properly structured. It's incorrect that the contact field should have a company_id field. That needlessly complicates all queries (raw SQL or Django) because an additional check is needed on the contact_id field.
On the other hand it's perfectly legit for two entities in a many to many relationship to have exactly one mapping instead of many. So there isn't one clear answer to this question. I suppose your best bet would be to add a ManyToMany field as well. I am making this suggestion purely on the fact that you say the data is read only
class Contact(models.Model):
uuid = models.CharField(max_length=36, primary_key=True, db_column='id')
company = models.ForeignKey(Company, db_column='company_id')
companies = models.ManyToManyField(Company, related_name='companies')
class Meta:
managed = False

Is it possible to instruct Django to save a model instance to a particular table based on its fields?

I'm attempting to construct a Django application that models an existing set of tables. These tables all have the same fields, plus custom fields per table. What I'm wanting to do is model this structure, and have records save to a particular table based on what table model they are attached to.
These tables can be created quite often, so it is unfeasible to construct new models per table.
Perhaps the code will demonstrate what I'm trying to do more clearly:
class CustomField(models.Model):
column_name = models.CharField(max_length=100)
description = models.CharField(max_length=255, blank=True, null=True)
class CustomData(models.Model):
custom_field = models.ForeignKey(CustomField)
value = models.CharField(max_length=100, blank=True, null=True)
# value will always be a nullable varchar(100)
class Table(models.Model):
table_name = models.CharField(max_length=255)
name = models.CharField(max_length=100)
custom_fields = models.ManyToManyField(CustomField)
class Record(models.Model):
table = models.ForeignKey(Table)
... list of common fields omitted ...
custom_values = models.ManyToManyField(CustomData)
When saving a new record that has a foreign key to 'table_1', I would like the eventual operation to be along the lines of insert into table_1 (..fields..) values (..field values..)
Is this possible? I guess I could hook into signals or the save method, but I'd like to find the simplest approach if such exists.
You can create unmanaged models dynamically. You just need to create a dict mapping column names to the data values. Once you have that, you can do the following:
from django.db import models
# This is the dict you created, mapping column names to values
values = {col_1: value_1, col_2: value_2, col_3: value_3, ... }
# Create a dict defining the custom field types, eg {col_name: django_field}
attrs = dict((c, models.CharField(max_length=100, blank=True, null=True)) for c in values)
# Add a Meta class to define the table name, eg table_1
class Meta:
app_label = myapp
db_table = 'table_1'
managed = False
attrs['Meta'] = Meta
attrs['__module__'] = 'path.to.your.apps.module'
DynamicModel = type('MyModel', (models.Model,), attrs)
# Save your data
DynamicModel.objects.create(**values)
Wrap this up in a function, and put it in your .save() method on Record. If you have any common fields, you can add them to attrs, or even better: create an abstract model with all the common fields and inherit that in the last line above instead of models.Model.