Django: overriding default field names in ManyToManyField - django-models

Good day,
Using a ManyToManyField as in the example below causes django to automatically create a Model called country_region with fields country_id and region_id, which is very cool. But what if the region_country database table (for reasons beyond the scope of this thread) has field names other than country_id and region_id?
Django generates the field names in the many-to-many table based on the model names of the related tables, so the only way I have found to achieve this is to change the two Model names. But that forces me to have model names that I don't want! How can I tell django what field names to use in the automatic many-to-many Model?
I have tried explicitly specifying the many-to-many Model using the through= keyword argument of the ManyToManyField, and this works, but then forms based on the Region Model do not allow saving...
Any advice greatly appreciated.
Thanks,
Randal
class Country(models.Model):
country_id = models.AutoField(primary_key=True)
country_name = models.CharField(max_length=200)
class Meta:
managed = False
db_table = 'country'
def __unicode__(self):
return '%s' % (self.country_name)
class Region(models.Model):
region_id = models.AutoField(primary_key=True)
region_name = models.CharField(max_length=200)
region = models.ManyToManyField(Country, db_table='region_country')
class Meta:
managed = False
db_table = 'region'

Related

SQLAlchemy: foreignKeys from multiple Tables (Many-to-Many)

I'm using flask-sqlalchemy orm in my flask app which is about smarthome sensors and actors (for the sake of simplicity let's call them Nodes.
Now I want to store an Event which is bound to Nodes in order to check their state and other or same Nodes which should be set with a given value if the state of the first ones have reached a threshold.
Additionally the states could be checked or set from/for Groups or Scenes. So I have three diffrent foreignkeys to check and another three to set. All of them could be more than one per type and multiple types per Event.
Here is an example code with the db.Models and pseudocode what I expect to get stored in an Event:
db = SQLAlchemy()
class Node(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
# columns snipped out
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
# columns snipped out
class Scene(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
# columns snipped out
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
# The following columns may be in a intermediate table
# but I have no clue how to design that under these conditions
constraints = # list of foreignkeys from diffrent tables (Node/Group/Scene)
# with threshold per key
target = # list of foreignkeys from diffrent tables (Node/Group/Scene)
# with target values per key
In the end I want to be able to check if any of my Events are true to set the bound Node/Group/Scene accordingly.
It may be a database design problem (and not sqlalchemy) but I want to make use of the advantages of sqla orm here.
Inspired by this and that answer I tried to dig deeper, but other questions on SO were about more specific problems or one-to-many relationships.
Any hints or design tips are much appreciated. Thanks!
I ended up with a trade-off between usage and lines of code. My first thought here was to save as much code as I can (DRY) and defining as less tables as possible.
As SQLAlchemy itself points out in one of their examples the "generic foreign key" is just supported because it was often requested, not because it is a good solution. With that less db functionallaty is used and instead the application has to take care about key constraints.
On the other hand they said, having more tables in your database does not affected db performance.
So I tried some approaches and find a good one that fits to my usecase. Instead of a "normal" intermediate table for many-to-many relationships I use another SQLAlchemy class which has two one-to-many relations on both sides to connect two tables.
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
noodles = db.relationship('NoodleEvent', back_populates='events')
# columns snipped out
def get_as_dict(self):
return {
"id": self.id,
"nodes": [n.get_as_dict() for n in self.nodes]
}
class Node(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
events = db.relationship('NodeEvent', back_populates='node')
# columns snipped out
class NodeEvent(db.Model):
ev_id = db.Column('ev_id', db.Integer, db.ForeignKey('event.id'), primary_key=True)
n_id = db.Column('n_id', db.Integer, db.ForeignKey('node.id'), primary_key=True)
value = db.Column('value', db.String(200), nullable=False)
compare = db.Column('compare', db.String(20), nullable=True)
node = db.relationship('Node', back_populates="events")
events = db.relationship('Event', back_populates="nodes")
def get_as_dict(self):
return {
"trigger_value": self.value,
"actual_value": self.node.status,
"compare": self.compare
}
The trade-off is that I have to define a new class everytime I bind a new table on that relationship. But with the "generic foreign key" approach I also would have to check from where the ForeignKey is comming from. Same work in the end of the day.
With my get_as_dict() function I have a very handy access to the related data.

which particular field of the model is referred under the foreign key in django's orm?

i have two classes 'topics' and 'webpage' and i'm trying to assign webpage.category as a foreign key referencing to topics.top_name.
But unlike raw sql where a foreign key can reference to a particular field in another table, in django's orm we just provide the referenced class'(table's) name and not the particular field the FK is referrring to.
class Topic(models.Model):
top_name = models.CharField(max_length=264, unique=True)
top_author = models.CharField(max_length=264)
class Webpage(models.Model):
category = models.ForeignKey(Topic)
name = models.CharField(max_length=264)
url = models.URLField()
You can set the to_field=… [Django-doc] in the ForeignKey constructor:
class Topic(models.Model):
top_name = models.CharField(max_length=264, unique=True)
top_author = models.CharField(max_length=264)
class Webpage(models.Model):
category = models.ForeignKey(Topic, to_field='top_name', on_delete=models.CASCADE)
name = models.CharField(max_length=264)
url = models.URLField()
As specified in the documentation, the field to which you refer should be unique (which of course makes sense, since otherwise, it would be ambiguous).
Note that usually, the collation [mysql-doc] of the referencing column and the target column should be the same. Otherwise it is not completely clear when the two fields are equal.
If you do not specify the to_field, it will use the primary key of the target model.

django Charfield suitable for a primary key?

In my django model I would like to set the field unique_id below to be the primary key as this will be the field used in queries for the model. It satisfies unique=True and null=False. However as django sets an AutoField to primary key in the background I'm unsure whether a CharField (which uses a unique 3 character code) is suitable as a primary key or whether this will be sub-optimal?
class PaymentMethod(models.Model):
unique_id = models.CharField(max_length=3)
last_updated = models.DateTimeField(auto_now=True)
There is nothing wrong with setting the CharField to be a primary key, by changing the model to be:
class PaymentMethod(models.Model):
unique_id = models.CharField(max_length=3, primary_key=True)
last_updated = models.DateTimeField(auto_now=True)
In actual fact if the unique_id is the field you will be querying it makes perfect sense to use it. Your other options are to use your existing model but with unique=True:
class PaymentMethod(models.Model):
unique_id = models.CharField(max_length=3, unique=True)
last_updated = models.DateTimeField(auto_now=True)
In this case your primary key will be an auto incrementing integer as you previously stated.
Another option depending on the number of records you are looking to store in the PaymentMethod models; and where the data is used elsewhere within your application. Is too look into using a model choices field. The model choices field might be on your payments or order model (depends on your application and what you are trying to do). This removes the need for a foreignkey and potentially reduces the number of queries in your app.
It could look something like this:
class Payment(models.Model):
VISA = 'VIS'
CREDIT = 'CRE'
MASTER_CARD = 'MAS'
PAYPAL = 'PAL'
PAYMENT_OPTIONS= (
(VISA, 'Visa'),
(CREDIT, 'Credit Card'),
(MASTER_CARD, 'Master Card'),
(PAYPAL, 'Paypal')
)
items = models.ForeignKey(Item)
date = models.DateField(auto_now=True)
...
payment_method = models.CharField(max_length=3, choices=PAYMENT_OPTIONS, default=VISA)
The PAYMENT_OPTIONS can be used to render dropdown boxes on the forms when using django model forms. Otherwise the users selection is limited to the options listed within this model.
This method would be a lot more efficient if you only have a small subset of PaymentMethod(s).
It's possible to use CharField as primary key. You just have to mark the field as primary key.
field_name = models.CharField(primary_key=True, max_length=100)
But I wouldn't recommend it because:
Primary keys are used in urls (typically in Rest APIs) - but not all characters are allowed in urls
DRF (django-rest-framework) use urls patterns that don't catch some characters by default (for example ".")
Primary keys must be unique - it's harder to accomplish it if the field is a string, especially when you let users to define it

Django, Direction of ManyToMany relations

I can't find the logic to which of the two related models should have the M2M field? Here is an example from the Django tutorial
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
what difference would it make if I define the M2M field in Person instead of Group ?
class Person(models.Model):
name = models.CharField(max_length=128)
groups = models.ManyToManyField(Person, through='Membership')
class Group(models.Model):
name = models.CharField(max_length=128)
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Technically, you can add a ManyToManyField on either model and get equivalent results, so the decision usually comes down to which model it makes more sense in, and that's usually a very subjective decision.
I usually decide by determining which model the relationship is more integral to: is the set of members more important to a Group than the set of groups is to the Person? If so, the relationship field should go on the Group model.

Django's ManyToMany Relationship with Additional Fields

I want to store some additional information in that, automatically created, ManyToMany join-table. How would I do that in Django?
In my case I have two tables: "Employees" and "Projects". What I want to store is how much each of the employees receives per hour of work in each of the projects, since those values are not the same. So, how would I do that?
What occurred to me was to, instead of the method "ManyToManyField", create explicitly a third class/table to store those new informations and to set its relationship with "Employees" and "Projects" using the "ForeignKey" method. I'm pretty sure it will work, but is this the best approach?
Here is example of what you want to achieve:
http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships
In case link ever breaks:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

Resources