How django creates internal tables dynamically for many-to-many fields - django

My model :
class Image(models.Model):
name=models.CharField(max_length=40,unique=True,help_text="name of the image")
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
tag = models.CharField(max_length=100,unique=True)
here when I do makemigrations and migrate it is creating 3 tables inside my database 1.image 2.tag 3.image_tags table
so, my question is i am not specifying image_tags table in my models.py file ,from where django is creating image_tags table and what is the flow ??
I have checked in migrations file but I didnot get any clarity regarding this

An intermediary table is required for a Many-To-Many relationship in a database, and because most of the time you don't need to store extra data on the relationship, Django just silently creates this table for you. In your case it will create a table with 3 fields: id, image_id, tag_id.
If you want to specify your own intermediary table, for example if you want to store extra data, you can create a model with ForeignKey's to your related tables and then define your ManyToManyField with a "through" argument like so:
class ImageTag(models.Model):
image = models.ForeignKey('Image')
tag = models.ForeignKey('Tag')
extra_data = models.CharField()
class Tag(models.Model):
name = models.CharField()
class Image(models.Model):
name = models.CharField()
tags = models.ManyToManyField(Tag, through=ImageTag)

Actually, that is not Django Functionality. It's SQL functionality. SQL is creating the internal table. Because SQL can't create reference, it's not Foreign key. That's why this Bridging table concept is emerged. It will resolve the problem as well as it will hold data(ID) of both tables and few custom fields, depends on requirements.
Updated with new req:
Refer this:
https://gist.github.com/jacobian/827937
Django 1.8 - Intermediary Many-to-Many-Through Relationship - What is the consequence of where 'ManytoManyField' is used?
From my point of view, It's not good practice to customize the bridging table. You can specify the extra fields to any of two tables orelse create a new table make it as foreign key

Related

Dynamic Model in Django from fields data in another model

I am working on a project where I have 2 models model
class Customer(models.Model):
name = models.CharField(...)
...
class CustomerProperty(models.Model):
name = models.CharField(...)
type = models.CharField(...)
code = models.CharField(...)
The CustomerProperty table has rows inside based on a parquet file (created at database init, not changed later on). For column in the parquet file, CustomerProperty has a row with column name from parquet as name in the table.
Now for some other purpose, I need to copy over all the data in the parquet file inside the db. So, for each row in the CustomerProperty table, this new table will have a column (plus one column for foreign key to Customer) so I can run filters based on property values. This will also be copied over at db init and not change later on, so it is an unmanaged django model.
I can't find any good docs on this, how to create a django model dynamically or how to add fields to that model dynamically. I don't need it to be managed, no need of migrations or django admin view support. I only need it to work with django ORM and be able to join and filter on it.
I've read the docs and didn't find much. Most of the answers talk about why this is a bad idea but I don't see any other way of solving my problem (I have few other tables joined together to run a complex query and I need to do further filtration based on these properties and support pagination.

create django models from existing sqlite db

I am used in creating orm and leaving django responsible for creating the tables.But in a project I am involved I have to create a simple CRUD application a frontend for an existing database. The database was created by creating the tables manually. So I have two tables Table1 and Table2 which have a many to many relationship through Tables12. Tables12 looks like the table that django would normaly create using a ManyToManyField thus it has two fields the id's of the two models. So after using django's inspectdb, django successfully created the models according to the SQLite database. The many to many tables like Tables12 was created like the following(as stated above):
class Tables12(models.Model):
table1 = models.ForeignKey(Table1)
table2 = models.ForeignKey(Table2)
class Meta:
managed = False
db_table = "Tables12"
unique_together = (("table1_id", "table2_id"),)
Trying the following gives me an error:
>> table2 = Table2.objects.get(pk=1)
>>tables12 = Tables12.objects.filter(table2=table2)
>>tables12
OperationalError: no such column: Tables12.id
I am guessing Django's orm is expecting an id field in every models created. How can I bypass this behavior? Is there a way to edit the tables so as they look more like django's orm but behave as the existing db's tables? Like:
class Table1(models.Model):
#pre exsiting fields
table2 = models.ManyToManyField(Table2)
or
class Table2(models.Model):
#pre existing fields
table1 = models.ManyToManyField(Table1)
but without destroying database records and without creating tables from start.
You can remove the Tables12 model, and specify the db_table argument to a ManyToManyField:
class Table1(models.Model):
tables2 = models.ManyToManyField(Table2, db_table='Tables12')
You would still not be able to query the Tables12 model directly (it still exists and has an id field), but this would allow you to use the JOINs Django generates for a ManyToManyField:
table1 = Table1.objects.get(pk=1)
tables2 = table1.tables2.all()
This would still not allow you to use Django to write to the join table, but it allows you to use the data that's in it. If at all possible, I'd strongly recommend adding a primary key.

How to write Foreign Key to an attribute of other model?

I want to create a model that joins two tables in my DB. When writing the foreign keys like this:
fromnode = models.ForeignKey(znode.code)
tonode = models.ForeignKey(znode.code)
there is an error: type object 'znode' has no attribute 'code', but there is such an attribute in znode:
class znode(models.Model):
code = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, primary_key=True)
How do I write this correctly?
Just use the class name znode instead of znode.code. Django automatically adds an id column to every model which will be used as reference as mentioned in the documentation.
Behind the scenes, Django appends "_id" to the field name to create its database column name. In the above example, the database table for the Car model will have a manufacturer_id column.
Also you should use CamelCaseClassNames to meet pep8 coding style conventions.

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.

ManyToManyField and South migration

I have user profile model with M2M field
class Account(models.Model):
...
friends = models.ManyToManyField('self', symmetrical=True, blank=True)
...
Now I need to know HOW and WHEN add each other as a FRIEND
And I created a model for that
class Account(models.Model):
...
friends = models.ManyToManyField('self', symmetrical=False, blank=True, through="Relationship")
...
class Relationship(models.Model):
""" Friends """
from_account = models.ForeignKey(Account, related_name="relationship_set_from_account")
to_account = models.ForeignKey(Account, related_name="relationship_set_to_account")
# ... some special fields for friends relationship
class Meta:
db_table = "accounts_account_friends"
unique_together = ('from_account','to_account')
Should I create any migration for this changes or not ?
If you have any suggestions you are feel free write their here.
Thanks
PS: accounts_account table already contain records
First off, I'd avoid using the db_table alias if you can. This makes it harder to understand the table structure, as it is no longer in sync with the models.
Secondly, the South API offers functions like db.rename_table(), which can be used by manually editing the migration file. You can rename the accounts_account_friends table to accounts_relation (as Django would name it by default), and add the additional columns.
This combined gives you the following migration:
def forwards(self, orm):
# the Account.friends field is a many-to-many field which got a through= option now.
# Instead of dropping+creating the table (or aliasing in Django),
# rename it, and add the required columns.
# Rename table
db.delete_unique('accounts_account_friends', ['from_account', 'to_account'])
db.rename_table('accounts_account_friends', 'accounts_relationship')
# Add extra fields
db.add_column('accounts_relationship', 'some_field', ...)
# Restore unique constraint
db.create_unique('accounts_relationship', ['from_account', 'to_account'])
def backwards(self, orm):
# Delete columns
db.delete_column('accounts_relationship', 'some_field')
db.delete_unique('accounts_relationship', ['from_account', 'to_account'])
# Rename table
db.rename_table('accounts_relationship', 'accounts_account_friends')
db.create_unique('accounts_account_friends', ['from_account', 'to_account'])
models = {
# Copy this from the final-migration.py file, see below
}
The unique relation is removed, and recreated so the constraint has the proper name.
The add column statements are easily generated with the following trick:
Add the Relationship model in models.py with foreign key fields only, and no changes to the M2M field yet.
Migrate to it
Add the fields to the Relationship model.
Do a ./manage.py schemamigration app --auto --stdout | tee final-migration.py | grep column
Revert the first migration.
Then you have everything you need to construct the migration file.
The way you've got it coded there, you're manually defining a model which does the same job as the m2m join table that Django will have automatically created for you. The thing is, the automatically created table will be called accounts_relationship_friend.
So, what you're doing there will create a model that tries to duplicate what the ORM has done under the surface, but it's pointing at the wrong table.
If you don't need an explicit join model, I would leave remove it from your codebase and not create a migration to add it, and instead use the M2M to find relationships between friends. (I'm not thinking about this too deeply, but it should work).
If, however, you want to do something special with the Relationship model you have (eg store attributes about the type of relationship, etc), I would declare the Relationship model to be the through model you use in your Friend.friends m2m definition. See the docs here.