Dynamic models django - python

Let's say i have 10 tables in 1 Db and the table names differ and data is different, but the structure of the tables are the same. And then i have 1 more table that collects all the table names and the creation date of that table.
Example:
PrimaryTable
table_name_1
table_name_2
....
table_name_10
and the structure of all tables example:
class PrimaryTable(models.Model):
name = models.CharField(db_column='Name', unique=True, max_length=100)
date = models.CharField(db_column='Date', max_length=100)
class Meta:
managed = True
db_table = 'Primary Table'
def __str__(self):
return self.name
class table_name_1(models.Model):
title = models.TextField(db_column='Title', blank=True, null=True)
url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
null=True)
description = models.TextField(db_column='Description', blank=True, null=True)
created_at = models.DateTimeField(db_column='Created_at')
class table_name_2(models.Model):
title = models.TextField(db_column='Title', blank=True, null=True)
url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
null=True)
description = models.TextField(db_column='Description', blank=True, null=True)
created_at = models.DateTimeField(db_column='Created_at')
and so on...
And i only want to make 1 class that includes all those tables that have same structure and call it when i choose the table from the PrimaryTable.
I don't want to use "python manage.py inspectdb > models.py" every time i create a table. I want to have access to the new created table instantly when i create it.

You can define your model fields as dict:
fields = dict(
title = models.TextField(db_column='Title', blank=True, null=True),
url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True, null=True),
description = models.TextField(db_column='Description', blank=True, null=True),
created_at = models.DateTimeField(db_column='Created_at'),
)
Then you can dynamicly create and run migration for it:
from django.db import connection, migrations, models
from django.db.migrations.executor import MigrationExecutor
def create_table(table_name, model_fields, app_label):
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name=table_name,
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
] + [(k, field) for k,field in model_fields.items()],
options={
'db_table': table_name,
},
),
]
executor = MigrationExecutor(connection)
migration = Migration(table_name, app_label)
with connection.schema_editor(atomic=True) as schema_editor:
migration.apply(executor._create_project_state(), schema_editor)
Then you can use dynamic model creation to access data in tables:
from django.db import models
def create_model(name, fields=None, app_label=None, module='', options=None, admin_opts=None):
"""
Create specified model
"""
class Meta:
db_table = name
if app_label:
# app_label must be set using the Meta inner class
setattr(Meta, 'app_label', app_label)
# Update Meta with any options that were provided
if options is not None:
for key, value in options.iteritems():
setattr(Meta, key, value)
# Set up a dictionary to simulate declarations within a class
attrs = {'__module__': module, 'Meta': Meta}
# Add in any fields that were provided
if fields:
attrs.update(fields)
# Create the class, which automatically triggers ModelBase processing
model = type(name, (models.Model,), attrs)
return model
I hope you will get general idea.
I took code from project that uses Django 2 (similar code is used to create tables per uploaded CSV file, columns and name of each uploaded CSV is stored in table which is similar to your PrimaryTable).

You can simplify things by keeping all of the data with the same structure in one table. You can use admin filters and custom manager methods to focus on one type of information at a time, which will provide the same functionality as separate tables.
class Information(models.Model):
name = models.CharField(db_column='Name', unique=True, max_length=100)
date = models.CharField(db_column='Date', max_length=100)
information_type = models.ForeignKey(InformationType, related_name='information_records')
def __str__(self):
return self.name
class InformationType(models.Model):
title = models.TextField(db_column='Title', blank=True, null=True)
url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
null=True)
description = models.TextField(db_column='Description', blank=True, null=True)
created_at = models.DateTimeField(db_column='Created_at')
def __str__(self):
return self.title

k i figure out but i dont know how to write it.
I m going to create 1 class in models:
Example models.py:
class Alltables(models.Model):
title = models.TextField(db_column='Title', blank=True, null=True) # Field name made lowercase.
url = models.CharField(db_column='Url', unique=True, max_length=250, blank=True,
null=True) # Field name made lowercase.
description = models.TextField(db_column='Description', blank=True, null=True) # Field name made lowercase.
created_at = models.DateTimeField(db_column='Created_at') # Field name made lowercase.
class Meta:
managed = False
db_table = 'Table_name' # make it args
def __str__(self):
return self.url
and in the class Meta i want to implement Args at db_table.
That means when i click on a table in the html i want that table name to be inserted in the db_table and give me the data in it.
Because the class PrimaryTable will give me all the table names in that DB.
I hope i ve explained k. But i still need the answer of how to implement args in the Meta class. and Use it in the views.py and html template.
tables/views.py
def table_base(request):
table_name = Crawledtables._meta.db_table
list_tables = Crawledtables.objects.order_by('id')
table_list = {'list_tables': list_tables}
return render(request, 'tables/table_base.html', {'table_name': table_name,
'list_tables': list_tables})
class AboutDetail(DetailView):
model = Crawledtables
pk_url_kwarg = 'table_id'
template_name = 'tables/table_list.html'
def get_object(self):
if 'table_id' not in self.kwargs:
return Crawledtables.objects.get(id=1)
else:
return Crawledtables.objects.get(id=self.kwargs['table_id'])
part of tables/views.py
class Details(ListView):
model = table_name_1
template_name = 'tables/table_list.html'
context_object_name = 'details'
paginate_by = 15
queryset = table_name_1.objects.all()

Related

How to return list of id's from create nested serialization using django rest framework

I created nested serialization but it is return only project name from project model but I want to return list of ids for one project.
Here you can find create nested serialization Created nested serialization
class Project(models.Model):
project_id = models.AutoField(primary_key=True, unique=True)
project_name = models.CharField(max_length=100)
class ProjectSite(models.Model):
site_id = models.AutoField(primary_key=True, unique=True)
site_name = models.CharField(max_length=200,name='project_site_name')
project_id = models.ForeignKey(Project, on_delete=models.CASCADE, blank=True, null=True,
related_name="projectid")
class Assignment(models.Model):
assignment_id = models.AutoField(primary_key=True)
assignment_name = models.CharField(max_length=150)
site_id = models.ForeignKey(ProjectSite,related_name="projectsiteidkey", on_delete=models.CASCADE)
assigned_to_id = models.ForeignKey('auth.User',related_name="assignedtoidfkey",on_delete=models.CASCADE)
I think you can use the SerializerMethodField to get those value.
class YourSerializer(serializers.ModelSerializer):
...
site_ids = serializers.SerializerMethodField(read_only = True)
...
def get_site_ids(self, obj):
return list(obj.projectid.values_list('id').distinct())

Django Rest API ManyToMany gives nothing [] in API

at the moment I try to get recipes from my API. I have a Database with two tables one is with recipes and their ids but without the ingredients, the other table contains the ingredients and also the recipe id. Now I cant find a way that the API "combines" those. Maybe its because I added in my ingredient model to the recipe id the related name, but I had to do this because otherwise, this error occurred:
ERRORS:
recipes.Ingredients.recipeid: (fields.E303) Reverse query name for 'Ingredients.recipeid' clashes with field name 'Recipe.ingredients'.
HINT: Rename field 'Recipe.ingredients', or add/change a related_name argument to the definition for field 'Ingredients.recipeid'.
Models
from django.db import models
class Ingredients(models.Model):
ingredientid = models.AutoField(db_column='IngredientID', primary_key=True, blank=True)
recipeid = models.ForeignKey('Recipe', models.DO_NOTHING, db_column='recipeid', blank=True, null=True, related_name='+')
amount = models.CharField(blank=True, null=True, max_length=100)
unit = models.CharField(blank=True, null=True, max_length=100)
unit2 = models.CharField(blank=True, null=True, max_length=100)
ingredient = models.CharField(db_column='Ingredient', blank=True, null=True, max_length=255)
class Meta:
managed = True
db_table = 'Ingredients'
class Recipe(models.Model):
recipeid = models.AutoField(db_column='RecipeID', primary_key=True, blank=True) # Field name made lowercase.
title = models.CharField(db_column='Title', blank=True, null=True, max_length=255) # Field name made lowercase.
preperation = models.TextField(db_column='Preperation', blank=True, null=True) # Field name made lowercase.
images = models.CharField(db_column='Images', blank=True, null=True, max_length=255) # Field name made lowercase.
#ingredients = models.ManyToManyField(Ingredients)
ingredients = models.ManyToManyField(Ingredients, related_name='recipes')
class Meta:
managed = True
db_table = 'Recipes'
When there is no issue it has to be in the serializer or in the view.
Serializer
class IngredientsSerializer(serializers.ModelSerializer):
# ingredients = serializers.CharField(source='ingredients__ingredients')
class Meta:
model = Ingredients
fields = ['ingredient','recipeid']
class FullRecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientsSerializer(many=True)
class Meta:
model = Recipe
fields = ['title','ingredients']
View
class FullRecipesView(generics.ListCreateAPIView):
serializer_class = FullRecipeSerializer
permission_classes = [
permissions.AllowAny
]
queryset = Recipe.objects.all()
This is at the moment my output
But I want e.g. the recipe with id 0 and all the ingredients which have also recipe id 0.
I really hope that you can help me. Thank you so much!
From the doc of ForeignKey.related_name,
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'.
So, change the related_name of Ingredients.recipeid field to
class Ingredients(models.Model):
# rest of the fields
recipeid = models.ForeignKey(
'Recipe',
models.DO_NOTHING,
db_column='recipeid',
blank=True,
null=True,
related_name="ingredients_ref" # Changed the related name
)
Then, migrate the database using python manage.py makemigrations and python manage.py migrate
Then, update your FullRecipeSerializer class as,
class FullRecipeSerializer(serializers.ModelSerializer):
ingredients_forward = IngredientsSerializer(many=True, source="ingredients")
ingredients_backward = IngredientsSerializer(many=True, source="ingredients_ref")
class Meta:
model = Recipe
fields = ['title', 'ingredients_forward', 'ingredients_backward']
Note that, here I have added two fields named ingredients_forward and ingredients_backward because there existing two types of relationships between Recipe and Ingredients and I am not sure which one you are seeking.

Many to Many model with a dropdown in Django Rest Framework?

I am trying to create a Many to Many relation with a model in between, I have a Client model, and a Zone model, each client may have access to different zones, and each zone may have multiple clients.
Therefore I created a model called Access Permission, that stores said relation, and I want to show a dropdown selector in the post form that shows the existing clients and zones, or to ask for the Id of an existing object, instead of showing the form to create new ones.
These are my models:
class Zone(models.Model):
name = models.TextField()
created = models.DateTimeField(auto_now=True)
def __str__(self):
return '%s' % (self.name)
class Client(models.Model):
name = models.TextField()
birthDate = models.DateField()
created = models.DateTimeField(auto_now=True)
def __str__(self):
return '%s' % (self.name)
class AccessPermission(models.Model):
idClient = models.ForeignKey(Client, on_delete=models.CASCADE, null=False)
idZone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=False)
And these my current serializers:
class ZoneSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Zone
fields = ('name',)
class ClientSerializer(serializers.HyperlinkedModelSerializer):
zones = ZonesSerializer(source='accesspermission_set', many=True, read_only=True)
class Meta:
model = Client
fields = ('name', 'birthDate', 'zones')
class AccessPermissionSerializer(serializers.ManyRelatedField):
idClient = ClientSerializer(many=False)
idZone = ZoneSerializer(many=False)
class Meta:
model = AccessPermission
fields = ('idClient', 'idZone')
Is there any way to ask for the Id of an existing object, or show the existing ones, instead of the fields to create new ones?
You can do it like:
models
class AccessPermission(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE, null=False)
zone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=False)
serializers
class AccessPermissionSerializer(serializers.ManyRelatedField):
id = serializers.IntegerField(read_only=True)
client_id = serializers.PrimaryKeyRelatedField(
queryset=Client.objects.all(), source='client', allow_null=False, required=True
)
zone_id = serializers.PrimaryKeyRelatedField(
queryset=Zone.objects.all(), source='zone', allow_null=False, required=True
)
class Meta:
model = AccessPermission
fields = (
'id', 'client_id', 'zone_id'
)

Django junction table with extra foreignkey

I have the following models
class Company(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe a company'
)
url = models.URLField('company URL', blank=True, null=True)
email = models.EmailField(blank=True, null=True)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'company'
verbose_name_plural = 'companies'
ordering = ['name', '-created_on']
def __repr__(self):
return '<Company {0.name}>'.format(self)
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe the project'
)
company = models.ForeignKey(
Company,
on_delete=models.PROTECT,
)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'project'
verbose_name_plural = 'projects'
ordering = ['-created_on', 'company']
permissions = (
("can_view_project",
"Can view all project related work"),
)
def __repr__(self):
return '<Project {0.name}>'.format(self)
def __str__(self):
return self.name
class Worker(models.Model):
description = models.TextField(
max_length=500,
blank=True,
help_text='Optional. Describe what the worker does or who they are'
)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
company = models.ForeignKey(Company)
class Meta:
order_with_respect_to = 'user'
def __repr__(self):
return '<Worker {0.id}'.format(self)
def __str__(self):
return self.user.get_fullname()
The problem
I would like to add a ManyToMany relationship between Project and Worker so that I can view
a list of workers under a certain project. However, I want to make sure that a worker can only
be added to a project if they are both part of the same company.
I was planning on using a junction table with a ForeignKey to both of their company attributes,
but according to the django docs, a foreignkey can only be used once per model
(https://docs.djangoproject.com/en/1.10/topics/db/models/#extra-fields-on-many-to-many-relationships)
How do I make sure that the many to many relationship between the two tables is limited to the same company?
Is there perhaps another way to ensure that workers cannot work on projects outside of their own company?
Assuming you define the many to many relationship this way in the Project model:
workers = ManyToManyField(Worker)
Assuming you have a model form named ProjectForm to create or modify projects. You can define a clean function in this form:
def clean(self):
cleaned_data = super(ProjectForm, self).clean()
for w in cleaned_data['workers']:
if w.company.id != cleaned_data['company'].id:
self.add_error('workers', your_error_message)
break
return cleaned_data
Hope this help.

Trying to insert a new column with a list of foreign field in django_tables2

I'm using the django_tables2 to show a list of "repair details". So far so good. However I want to add a new field (it's not in the model): a list of service reports. The relation is 1 to many. and show the serial number which is in another model. It's quite difficult since I add the field but the render_NEW_FIELD doesn't work and i couldn't reach the list of service reports since in the table model I have just access to the record and not to the repair_detail model. I'm using django_tables 0.15
repair/models.py
class RepairDetail(models.Model):
repair = models.ForeignKey('Repair')
repair_detail_number = models.IntegerField()
article_code = models.CharField(max_length=30)
registered_date = models.DateField(null=True)
registered_uid = models.IntegerField(null=True)
registered_initials = models.CharField(max_length=2, null=True)
customer_reference = models.CharField(max_length=20, null=True)
db2_recnum = models.IntegerField()
def __unicode__(self):
return str(self.repair_detail_number)
class ServiceReport(models.Model):
repair_detail = models.ForeignKey('RepairDetail')
status = models.ForeignKey('ServiceReportStatus', on_delete=models.PROTECT)
serial_number = models.ForeignKey('core.SerialNumber')
customer = models.ForeignKey('core.Company')
contact = models.ForeignKey('core.Contact', null=True, on_delete=models.SET_NULL)
project = models.ForeignKey('core.Project')
project_code = models.CharField(max_length=4)
identifier = models.CharField(max_length=1)
repair_date = models.DateField(null=True)
repair_uid = models.IntegerField(null=True)
repair_initials = models.CharField(max_length=2, null=True)
booking_date = models.DateField(null=True)
core/models.py
class SerialNumber(models.Model):
product = models.ForeignKey("Product")
panel = models.ForeignKey("Panel", null=True, blank=True, default = None)
serial_number = models.CharField(max_length=30, default='')
manifest = models.TextField(null=True)
def __unicode__(self):
return self.serial_number
repair/tables.py
class RepairDetailTable(tables.Table):
#serials = tables.Column(accessor='servicereport.serialnumber.serialnumber')
serial = tables.Column()
def __init__(self, *args, **kwargs):
super(RepairDetailTable, self).__init__(*args, **kwargs)
class Meta(object):
model = RepairDetail
fields = ('id', 'repair_detail_number', 'article_code', 'registered_date', 'registered_initials', 'customer_reference', 'serials')
attrs = {'class': 'table table-striped table-bordered protonic'}
empty_text = "No records found."
If you want (1:N instead of N:1) to concatenate all related models in the table cell try to do this in your model layer by setting property, something like this:
class RepairDetail(models.Model):
# ...
#property
def service_reports_list(self):
return self.servicereport_set.all()
Define your own column (inherit tables.Column) and override render method
Add it to your table class:
class RepairDetailTable(tables.Table):
# ...
service_reports_list = YourOwnColumn(some_kwargs)
# ...

Categories

Resources