Django model for same structured databases - django

I have different data databases of same database schema (also in 1 database: different tables with same structure/schema) and I want use those databases in all my other apps as data backend.
For example,
database name: database1
class tableA(models.Model):
a = models.CharField()
b = models.CharField()
class Meta:
db_table = 'tableA'
class tableB(models.Model):
c = models.CharField()
d = models.CharField()
class Meta:
db_table = 'tableB'
class tableC(models.Model):
c = models.CharField()
d = models.CharField()
class Meta:
db_table = 'tableC'
database name: database2
class tableA(models.Model):
a = models.CharField()
b = models.CharField()
class Meta:
db_table = 'tableA'
class tableB(models.Model):
c = models.CharField()
d = models.CharField()
class Meta:
db_table = 'tableB'
class tableC(models.Model):
c = models.CharField()
d = models.CharField()
class Meta:
db_table = 'tableC'
Here, you can see database1 and database2 is having same schema. Also in both databases, tables: tableB and tableC having same schema. In short there is seperate database created for each region with same structure instead of 1 big database for all regions. In 1 database I have around 15 tables and out of 15, 12 tables having same schema in which daily data is stored.
Can anyone please tell me how should I design this in django? Should I create 1 app with multiple model files (1 for each database) and direct it to different databases with router? How? Or create different app for each database? You can see in both cases there is a lot of code redudency as all model files having same structure.

Related

Common fields for different django models in one place

I have some columns that are repeated in multiple models. is there any solution to place them somewhere and use it any model?
You can achieve this by creating base classes and inheriting them in your models.
Example:
class TimestampsModel(models.Model):
#classmethod
def get_fields(cls, fields: tuple):
return fields.__add__(('created_at', 'updated_at'))
created_at = models.DateTimeField(("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(("updated_at"), auto_now=True)
You can also make it abstract and Django won't create migrations for this.
class Meta:
abstract = True
Finally, a model would be:
class Blog(baseModels.TimestampsModel):
class Meta:
db_table = "blog"
title = models.CharField(max_length=100)

How to avoid repetition in Django model fields?

I have models that share many common fields. For example:
class Customer(models.Model):
name = models.CharField()
email = models.CharField()
address = models.CharField()
phone = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
wallet = models.FloatField()
class Seller(models.Model):
# same fields from Customer class, except the field wallet
To avoid repeating these fields, I have tried to create classes with these common fields and link using OneToOneField:
class ContactInformation(models.Model):
phone = models.CharField()
email = models.CharField()
class AddressInformation(models.Model):
address = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
class Customer(models.Model):
wallet = models.FloatField()
contact_information = models.OneToOneField(ContactInformation)
address_information = models.OneToOneField(AddresssInformation)
class Seller(models.Model):
contact_information = models.OneToOneField(ContactInformation)
address_information = models.OneToOneField(AddresssInformation)
But now it gets very messy if I try to create a ModelForm based on the Customer, as there is only the wallet field in it. To display my other OneToOneFields I have to create multiple forms: a form for the contact information and another for address information, as ModelForms don't simply display these OneToOneFields as a single form. The views get bloated, as I have to validate 3 forms in total and have to manually create the object instances.
Am I missing something here? Should I use inheritance instead? Should I just repeat these fields to have simpler forms and views? Any advice would be greatly appreciated.
Take a look at abstract base classes, it provides a clean way to reuse common fields to multiple tables.
You might consider:
from django.db import models
class CommonUserInfo(models.model)
name = models.CharField()
email = models.CharField()
address = models.CharField()
phone = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
class Meta:
abstract = True
class Customer(CommonUserInfo):
wallet = models.FloatField()
class Seller(CommonUserInfo):
pass
I am not sure what the benefit of using a foreign key for address information is unless you have multiple customers/sellers using the same address and the addresses will need to be updated in unison.

How do I populate FOREIGN KEY with a Django form

I am trying to give the user the ability to enter data
here is my models named cv :
class Experience_Pro(models.Model):
annee_debut = models.IntegerField()
annee_fin = models.IntegerField()
description_exp_pro = models.TextField(null=True,blank=True)
class Ecole(models.Model):
nom_ecole = models.CharField(max_length=50)
classement = models.IntegerField()
class Academic(models.Model):
annee_debut = models.IntegerField()
annee_fin = models.IntegerField()
type_diplome = models.CharField(max_length=10)
description_academic = models.TextField(null=True,blank=True)
ecole = models.ForeignKey('Ecole' , on_delete=models.DO_NOTHING)
class Cv(models.Model):
experience_Pro = models.ForeignKey('Experience_Pro' ,on_delete=models.CASCADE)
academic = models.ForeignKey('Academic',on_delete=models.CASCADE)
and here is my form
class CvForm(forms.ModelForm):
class Meta:
model = Cv
fields = "__all__"
but instead of getting inputs for the user to enter data i get a dropdownlist of already existed records in my database.
Unfortunately, that is the way django is designed when using a ModelForm.
Realistically, you can create two model forms from the Experience & Academic models and join them in the view.
Multiple Models in a single django ModelForm?
Your cv model doesnt have any fields for input. You are only have the relation with other model as a foreignkey relation. This way you can only access the data here not create one

Django ManyToMany Validation Constraint

I have a ManyToMany link, and a Foreign key which links three objects.
[A]>--<[B]>---[C]
A can belong to many of B, and vice versa. However, A can only belong to B objects with the same parent C.
I'm trying to do something in the clean() method of the model. I'm using Django Rest Framework and no ModelForms or anything like that. I haven't been able to figure it out yet
Simplified Sample Code
class Device(models.Model):
name = models.CharField(max_length=20)
projects = models.ManyToManyField(Project, 'devices')
details = models.CharField(max_length=200)
serial = models.CharField(max_length=20)
address models.GenericIPAddressField(default="0.0.0.0")
port = models.IntegerField(default=3000)
jumpers = models.IntegerField(default=0)
install_date = models.DateField(blank=True, null=True)
class Project(models.Model):
name = models.CharField(max_length=20)
description = models.CharField(max_length=250)
area = models.ForeignKey(Area)
class Area(models.Model):
name = models.CharField(max_length=20)
description = models.CharField(max_length=250)
owner = models.CharField(max_length=20) # microservice doesn't have owner group - field in JWT
Serializers
class AreaSerializer(serializers.ModelSerializer):
class Meta:
model = Area
fields = ('name', 'description', 'owner')
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('id', 'name', 'description', 'area')
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = ('id', 'name', 'projects', 'details', 'serial',
'address', 'port', 'jumpers', 'install_date')
I am not sure where and how do you want to validate your data. So I am just posting the method which can validate if a project can be linked to a device or not based on your specific check.
def validate_project(device, project):
projects = device.projects.all()
areas = set(projects.values_list('area', flat=True))
if len(areas) > 1:
raise serializers.ValidationError('projects are not valid')
return areas.pop() == project.area_id
EDIT:
You have to use a intermediate model for storing the relationship between device and project.
class Membership(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
area = models.ForeignKey(Area, on_delete=models.CASCADE)
use the above membership model to store the many to many relations.
On your device model use this field to define the many to many relation.
projects = models.ManyToManyField(Project, through='Membership')
checkout the docs
Now when you link a device and project you will have explicitly add the area id as well. Before adding now you can check if the project is valid or not based on the area associated.
(ignore the wonky field types, cba)
What it boils down to is: you need a table BC that stores relations between B and C. Table A would then select only from those relations through the intermediary m2m table ABC (or ditch ABC, couldn't figure out how to draw m2m with the online tool). I think I mixed up B and C in this picture, swap them around depending on whether B or C holds the ForeignKey.
Please correct if I'm wrong!

Django Rest Framework : managing relations

I have the following 3 tables
movies
ID | shortDesc | uploaded
sources
ID | name | createDate
movieSources
movieID | sourceID
How do I link them all together to get all the data in one REST call
I have the below code and it doesnt seem to work
models.py
class movieSources(models.Model):
class Meta:
db_table = "movieSources"
movieID = models.IntegerField()
sourceID = models.IntegerField()
class movies(models.Model):
class Meta:
db_table = 'movies'
uploaded = models.DateTimeField('date published')
sourcesMovie = models.OneToOneField(movieSources)
class sources(models.Model):
class Meta:
db_table = 'sources'
createDate = models.DateTimeField('date published')
Serializers.py
class MovieSourcesSerializer(serializers.ModelSerializer):
class Meta:
model = movieSources
fields = ('movieID', 'sourceID')
class MoviesSerializer(serializers.ModelSerializer):
sourceID = serializers.IntegerField(source='sourcesMovie.sourceID')
class Meta:
model = movies
fields = ('id','uploaded', 'sourceID')
First of all you have to rethink the design of your models. As described in the opening, there is a many-to-many relationship between movies and sources. movieSources would be the intermediate table, which isn't necessary to be declared as a model, as Django's ORM takes care about that.
Also I would suggest you to follow the conventions and write your model class names in PascalCase. Use rather singular names for this purpose.
models.py
class Movie(models.Model):
short_desc = models.CharField('short description', max_length=255)
uploaded = models.DateTimeField('date published', auto_now=True)
class Meta:
db_table = 'movies'
class Source(models.Model):
name = models.CharField(max_length=50)
create_date = models.DateTimeField('date published', auto_now=True)
movies = models.ManyToManyField(Movie, related_name='sources')
class Meta:
db_table = 'sources'
So should the models look like according to your description. The option auto_now=True will create automatically the timestamp when an entry is created. The models are connected with a m:n relation. Django takes care for everything else behind the doors.
Now you can create your serializers:
serializers.py
class MovieSerializer(serializers.ModelSerializer):
sources = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Movie
fields = (short_desc, uploaded, sources)
class SourceSerializer(serializers.ModelSerializer):
movies = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Source
fields = (name, create_date, movies)
The PrimaryKeyRelatedField will fetch the related data.
I didn't test the code and wrote it as I remember, but I think it should work out of the box. I hope the answer helps you and gives you an idea how to solve your problem better.