I am seeking to create a relational database design in Django where one table has relationships with multiple models in the DB.
The sample models are excerpted below.
from __future__ import unicode_literals
from django.db import models
class State(models.Model):
state_name = models.CharField(max_length=100, unique=True)
class District(models.Model):
state_id = models.ForeignKey(State, on_delete=models.CASCADE)
district_name = models.CharField(max_length=100)
class County(models.Model):
county_id = models.ForeignKey(County, on_delete=models.CASCADE)
district_id = models.ForeignKey(District, on_delete=models.CASCADE)
county_name = models.CharField(max_length=100, unique=True)
class Kiosk(models.Model):
county_id = models.ForeignKey(County, on_delete=models.CASCADE)
kiosk_name = models.CharField(max_length=100)
kiosk_type = models.CharField(max_length=100)
kiosk_size = models.CharField(max_length=100)
class Operator(models.Model):
kiosk_id = models.ForeignKey(County, on_delete=models.CASCADE)
operator_name = models.CharField(max_length=100)
The overall goal is to register kiosks and their operators in the administrative territories. All relationships between the models are one-to-many. Administrative territories are hierarchical from the States-Counties-Townships which according to the schema design leads to one table having many foreign keys. For example Township(state_id, county_id) and Kiosk(state_id, county_id, township_id) and so forth.
If such a design is appropriate, then how would i model it in Django such that a single model like kiosk has 2 or 3 foreign keys relating to the other models?
If i attempt to add foreign keys as it appears in the County model i get the following error on applying migrations.
You are trying to add a non-nullable field 'region_id' to county without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option:
It is obviously not the way to do it and i am seeking guidance from anyone who might have a solution to this problem.
I am working in Django 1.10.
Thank you all.
Related
Can you give me an example of how to use the software library SQLAlchemy ORM? In particular, how do I build standard database relationships like "One to Many" and "One to One"?
I know that the SQLAlchemy documentation already provides some examples at Basic Relationship Patterns , but I'm looking for examples that explain what's happening for the beginner user and especially discussing tradeoffs that need to be considered.
I've created some examples / templates with explanatory comments:
( a heavier formatted version is here )
# Building 1-to-Many Relationship
# https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html#one-to-many
# back_populates() targets are class attribute names.
# The example is made clearer using my data type prefix notation and specifically o_ as class attributes (but o_ are not table columns!)
# Note the difference between parent_id (integer) and o_parent_obj (sqla object)
# Note: l_children_list is a list of sqla objects.
# Read back_populates() as "this relationship back populates as the following class attribute on the opposing class"
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
l_children_list = relationship("Child", back_populates="o_parent_obj") # not a table column
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
o_parent_obj = relationship("Parent", back_populates="l_children_list") # not a table column
# Building 1-to-1 Relationship
# https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html#one-to-one
# Two changes:
# To convert this to “one-to-one”, the “one-to-many” or “collection” side is converted into a scalar relationship using the uselist=False flag.
# Add unique constraint (optional)
#
# Child.o_parent_obj will be 1-to-1 because there is only 1 value in the Child.parent_id column.
#
# Parent.o_first_child will be 1-to-1 at the ORM level, because ORM forces the value to be a scalar via uselist=False flag.
# Tip in docs: 1-to-1 enforcement at the db level is also possible and can be considered:
# This is a db design decision because it's a trade off: it provides referential integrity at the db level but at the cost of an additional db index.
# Enforce 1-to-1 for Parent.o_first_child at the db level as follows:
# put unique constraint on Child.parent_id column to make sure all Child rows point to different Parent rows.
# Note: this unique constraint is different from the foreign key designation because foreign key is uniqueness on the Parent table (not the Child table).
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
o_first_child = relationship("Child", back_populates="o_parent_obj", uselist=False ) # uselist=False enforces 1-to-1 at ORM level
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'), unique=True ) # unique constraint enforces 1-to-1 at db level. Optional. Creates db index.
o_parent_obj = relationship("Parent", back_populates="o_first_child")
# Building 1-way bookkeeping properties Relationship. "many-to-one"
"""
In the db schema design, sometimes there are "bookkeeping" property fields whose value is a row in another table.
These are typically less important fields that are not part of the core db design.
Rather, they are more like bookkeeping properites and the data type for this field is another table.
For example, created_by field to track which user created the data entry.
The python code might want to get the user who created the data, but it won't start from the user to get all the data entries that he created.
Thus, these are called "1-way bookkeeping properties".
Oh, there is a name for these: "many-to-one" relationships, but even the top google search results are poor at explaining them.
How to build a "1-way bookkeeping property"?
"""
class UserAccount(Base):
__tablename__ = 'user_account'
id = Column(Integer, primary_key=True)
# UserAccount does not track any of the bookkeeping properties that point to it.
class SomeData(Base):
__tablename__ = 'some_data'
id = Column(Integer, primary_key=True)
i_user_who_created_the_data = Column(Integer, ForeignKey('user_account.id'))
o_user_who_created_the_data = relationship("UserAccount", foreign_keys="[SomeData.i_user_who_created_the_data]") # 1-way ORM ability: from SomeData to UserAccount
i_user_who_last_viewed_the_data = Column(Integer, ForeignKey('user_account.id'))
o_user_who_last_viewed_the_data = relationship("UserAccount", foreign_keys="[SomeData.i_user_who_last_viewed_the_data]") # 1-way ORM ability: from SomeData to UserAccount
I don't think I fully understand association tables. I know how to work with normal tables i.e add rows and what not but I don't understand how to work with an association table.
why would I use the below
student_identifier = db.Table('student_identifier',
db.Column('class_id', db.Integer, db.ForeignKey('classes.class_id')),
db.Column('user_id', db.Integer, db.ForeignKey('students.user_id'))
)
Vs
class studentIdent(db.model):
db.Column(db.Integer, db.ForeignKey('classes.class_id')),
db.Column(db.Integer, db.ForeignKey('students.user_id'))
As mentioned in a comment to the question, you would not bother creating a class for the association table if it only contains the foreign keys linking the two tables in the many-to-many relationship. In that case your first example – an association table – would be sufficient.
However, if you want to store additional information about the nature of the link between the two tables then you will want to create an association object so you can manipulate those additional attributes:
class StudentIdent(db.Model):
__tablename__ = "student_identifier"
course_id = db.Column(
db.Integer,
primary_key=True,
autoincrement=False,
db.ForeignKey('courses.course_id')
)
user_id = db.Column(
db.Integer,
primary_key=True,
autoincrement=False,
db.ForeignKey('students.user_id')
)
enrolment_type = db.Column(db.String(20))
# reason for student taking this course
# e.g., "core course", "elective", "audit"
and then you could create the link between a given student and a particular course by creating a new instance of the association object:
thing = StudentIdent(course_id=3, user_id=6, enrolment_type="elective")
Note: This is just a basic linkage. You can get more sophisticated by explicitly declaring a relationship between the ORM objects.
I apologize for the novice question but my head is about to explode.
I am trying to learn Django and wanted to create something practical and that I could use. I settled with a small inventory system.
The problem I am having is figuring out the best way to have relationships between models for ideal db setup.
I have models for the following:
Depreciation Policy
Manufacturer
Customer/Owner
Asset Category (Server, laptop etc)
Asset Model (Macbook Pro, Proliant DL380 Gen 9 etc)
Asset Status (Archived, Deployed, Lost etc)
Asset Fields (Generic fields all assets would
have, model(FK), status(FK), purchase date etc.)
Cpu
Server
Network Card
Right now I have both Server & Network Card inheriting Asset Fields.
My goal was to have tables for each type of asset but still have one master table that can reference each asset type's table via a FK so that if I want to display all assets in the database I can just list one table and pull in the relevant information from their related tables instead of having to look through each asset's table.
I want something like:
Asset Table:
id(pk), model(fk), status(fk), serial Number, purchase date, cost, location, server_id/network_card_id(fk)
Server Table:
id(pk), customer, name, memory, cpu(fk), ilo_type, ilo_lic
Asset Model:
class Asset(models.Model):
# class Meta:
# abstract = True
assetTag = models.CharField(max_length=50, unique=True)
model = models.ForeignKey(AssetModel, on_delete=models.SET_NULL, null=True)
status = models.ForeignKey(AssetStatus, on_delete=models.SET_NULL, null=True)
serialNumber = models.CharField(max_length=50, unique=True)
purchaseDate = models.DateTimeField('Date order was placed', blank=True)
purchaseOrder = models.CharField(max_length=25, blank=True)
cost = models.DecimalField(default=0, max_digits=8, decimal_places=2, blank=True)
# location = models.CharField(max_length=25, blank=True, null=True)
def calculate_current_value(self, purchaseDate, cost):
purchase_price = float(cost)
months = self.model.depreciationPolicy.months
if months > 0:
days = months * 30
depreciation_per_day = (float(cost) / days)
days_owned = timezone.now() - purchaseDate
value_lost = days_owned.days * depreciation_per_day
current_asset_value = purchase_price - value_lost
return round(current_asset_value, 2)
def __str__(self):
return self.serialNumber
Server Model:
class Server(Asset):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True)
memory = models.IntegerField(default=0)
cpu = models.ForeignKey(Cpu, on_delete=models.SET_NULL, null=True)
ILOType = models.CharField(max_length=50, choices=(('Std', 'Standard'), ('Adv', 'Advanced')))
ILOUsername = models.CharField(max_length=50, null=True, blank=True)
ILOPassword = models.CharField(max_length=100, null=True, blank=True)
ILOLicense = models.CharField(max_length=50, null=True, blank=True)
def __str__(self):
return self.serialNumber
Network_Card Model:
class Nic(Asset):
macAddress = models.CharField(max_length=100, unique=True)
portCount = models.IntegerField(default=0)
portType = models.CharField(max_length=50, choices=(('Ethernet', 'Ethernet'), ('Fiber', 'Fiber')))
def __str__(self):
return self.name
The first thing I would recommend is for you to just forget about tables. You're not dealing with tables in Django, but with models, which are classes that represent the entities of your system. Models become tables later, but you don't have to concern yourself with them right now.
Second, model your classes carefully. Design a nice diagram representing their relationships. In your scenario, one class will contain references to other classes (a pivot class), so model that.
Also, take a moment to read the documentation. Django is a very neatly and thoroughly documented. Read carefully about models and squerysets. You will know everything you need to represent things inside your architecture.
Some hints:
When defining foreign fields, you'll have quite a few options, from ForeighKey(), ManyToManyField(), ManyToOneRel(), etc. Read about each and every one of the options, and chose the one that represents your reality most accurately.
ManyToMany() has an interesting behavior, which is: if you don't pass a table to it, it will create one for you, to act as a pivot. I prefer to create my middle tables, as it is more aligned with the Zen of Python.
When you want to return a coherent representation of your data, you'll have to work with querysets in order to enforce the relationships you have built into your models, much in the same way you'd do with a relational database, either by constructing a query, or designing a view.
Here's some nice links for you:
Fields: https://docs.djangoproject.com/en/1.10/ref/models/fields/
Querysets: https://docs.djangoproject.com/en/1.10/topics/db/queries/
The Zen of Python: https://www.python.org/dev/peps/pep-0020/
Further, I strongly recommend you to go take a look at Django Rest Framework as soon as you get a hold on the basic concepts of Django models. Here's the link: http://www.django-rest-framework.org/
And come back to me when you have more specific questions.
Happy coding!
I believe I have solved this issue.
the solution was to build it out as follows. Instead of the Server & Nic class inheriting the Asset class, I defined a 1:1 relationship with each of them and the Asset class.
asset = models.OneToOneField(Asset, on_delete=models.CASCADE, primary_key=True)
This allowed for the Asset table to track all assets, and the asset_id(PK) is both a foreign_key and a primary_key in Server & Nic.
I'm trying to figure out if there is any efficient way to serialize a queryset from superclass. My models:
class CampaignContact(models.Model):
campaign = models.ForeignKey(Campaign, related_name="campaign_contacts", null=False)
schedule_day = models.DateField(null=True)
schedule_time = models.TimeField(null=True)
completed = models.BooleanField(default=False, null=False)
class CampaignContactCompany(CampaignContact):
company = models.ForeignKey(Company, related_name='company_contacts', null=False)
class CampaignContactLead(CampaignContact):
lead = models.ForeignKey(Lead, related_name='lead_contacts' ,null=False)
I want to create a json with all campaign contacts may it be leads' or companys'
Django has a built in serializer documented here but it might not work as well considering how you structured your models:
from django.core import serializers
data = serializers.serialize("json", CampaignContactCompany.objects.all())
I imagine you could run that on both tables and combine the two sets but it would introduce a bit of overhead. You could also create a static to_json method in CampaignContact which takes two query sets from the other two tables and formats/combines them into json.
Maybe you have reason to model your tables as you did but based on observation it looks like you will have 3 tables, one never used and two with only a company and lead field different which is probably not ideal. Typically when relating a record to multiple objects you would simply put the lead and company field on the CampaignContact table and let them be null. To get only company contacts you could query company_contacts = CampaignContact.objects.filter(company__isnull=False).all()
** Below are two models teacher and loginstudent, A teacher can teach multiple sections and multiple students can be in same section.So section field cannot be a foreign key.If I want to find out all courses taken by a particular student,what should I do? Is there any simple django query just like sql did.How to do?
**
class Teacher(models.Model):
username=models.CharField(max_length=50)
password=models.CharField(max_length=89)
course=models.CharField(max_length=30)
section=models.CharField(max_length=30)
class LoginStudent(models.Model):
username=models.CharField(max_length=50)
password=models.CharField(max_length=89)
section=models.CharField(max_length=30)
OK, I would recommend to stick to the default user system from Django and build one-to-one profiles of the specific type where needed. Latter on you can differentiate between the users based on the value of the foreign key or you could implement permissions which are tied to the user type
from django.db.models.query_utils import Q
# for example, this could be a way to extend users to hold teachers and students
class TeacherProfile(models.Model):
user = models.OneToOneField(User, related_name='teacher_profile')
# other relevant teacher profile items
ONLY_TEACHERS_FILTER = Q(teacher_profile__isnull=False) & Q(student_profile__isnull=True)
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='student_profile')
# other relevant student profile items
sections = models.ManyToManyField('Section', related_name='students') # mind the quotes to Section name
class Section(models.Model)
name = models.CharField(max_length=50)
# other section fields goes here...
class Course(models.Model):
name = models.CharField(max_length=50)
teacher = models.ForeingKey(User, related_name='courses', limit_choices_to=ONLY_TEACHERS_FILTER)
sections = models.ManyToManyField(Section, related_name='courses')
Now to answer to the question what are the courses to which a student attends to:
queryset = Course.objects.filter(section__students__in=[user])
Hope it helps!