I know I can change table name per model using db_table attribute, as explained in the doc.
However, what if I want to do it globally? Let's say, I want all my table to be like:
db_table = f"table_{{model_name}}"
Is there any setting for this?
Otherwise, I guess I could use a mixin to be inherited from. However, the whole purpose of doing this is to NOT think of table naming when any developer will add a new model.
After reading this issue, It seems maintainers didn't even consider to add this feature as they did not understand the need, which is pretty clear to me :/
Any idea? Thanks.
The default database table naming goes like db_table = f"{app_label}_{model_name}" and you can rewrite them As hedde-van-der-heide suggested, by creating an abstract base model and subclassing them on your models, like
from django.db import models
class BaseModel(models.Model):
class Meta:
abstract = True
db_table = f"table_{model_name}"
class NewModel(BaseModel):
name = models.CharField(max_length=128)
def __str__(self):
return str(self.name)
Related
I want to create 73 different django models, those models will be very similar, so in theory I would inherit from a base model class and just change the name of the Model/table.
I am aware this is not the best database structure, however I believe this unconventional structure may improve other aspects of my application. The initial point is to test this hypothesis.
How can I have django create the models, without me having to define all 73 of them?
class BaseModel(models.Model):
some_field = models.CharField(max_length=255)
some_other_field = models.CharField(max_length=255)
class Model_NR_01(BaseModel):
pass
...
class Model_NR_73(BaseModel):
pass
Also, in the sample above, I believe the BaseModel would also be created. How could I prevent that, having at the end of the migration only the 73 models mentioned? (If possible, of course).
PS.: I did searched several similar questions, couldn't find an actual answer, only warnings of how bad design it is. I am aware.
The three argument form of type can be used to create classes dynamically. The only thing you need to pass in the attributes dictionary is __module__ as using type this way to create a class will not automatically populate this attribute which is required by Django
class BaseModel(models.Model):
some_field = models.CharField(max_length=255)
some_other_field = models.CharField(max_length=255)
class Meta:
abstract = True
for i in range(1, 74):
model_name = f'Model_NR_{i:02}'
globals()[model_name] = type(model_name, (BaseModel, ), {'__module__': BaseModel.__module__})
I have to models
class Parent(object):
text_field = models.TextField()
boolean_field = models.BooleanField()
class Child(Parent):
another_text_field = models.TextField()
With the following ModelAdmin
class ChildAdmin(admin.ModelAdmin):
pass
admin.site.register(Child, ChildAdmin)
I currently see all fields in the admin page, i.e. text_field, boolean_field, and another_text_field.
Question: How can I get a parent select field and exclude text_field and boolean_field (for latter I guess I can use exclude).
Current Solution: I add a Form and use its clean method to set the parent field. text_field and boolean_field can be excluded by addind it to the ModelAdmin's excluded variable.
simply overwrite fields
class Child(Parent):
another_text_field = models.TextField()
text_field = None
boolean_field = None
if you want to use inheritance in django models use abstract models please.
I am not sure if it is really necessary to use model inheritance. if not, you may consider using OneToOneField without model inheritance.
Example using OneToOneField:
models.py
class Parent(models.Model):
text_field = models.TextField()
boolean_field = models.BooleanField()
class Child(models.Model):
parent = models.OneToOneField(Parent,
on_delete=models.CASCADE,
primary_key=True)
child_field = models.TextField()
admin.py
#admin.register(Parent)
class ParentAdmin(admin.ModelAdmin):
pass
doing so you can see a drop down menu for picking Parent instance at child admin page. but meanwhile, you lose one 'benefit' of using inheritance, which is the availability of Parent field in Child
as mentioned in the docs,
All of the fields of Place will also be available in Restaurant,
although the data will reside in a different database table.
but there is a easy fix for that, just use something like Child.objects.filter(parent__text_field="something"). Query performance should be the same (I guess) since implementation in db are basically the same for these two approaches (both use separated table) (please correct if I am wrong)
Apart from from this and admin display behavior, I am not sure how these two approaches (your approach and this answer) are differed.
I'm trying to alter an app I've created so that it is reusable. It's based around a single model which sites using the app will subclass. As it stands, my non-reusable version has the following kind of structure:
# models.py
class Document(models.Model):
contents = models.TextField()
date = models.DateTimeField()
# views.py
from .models import SiteModel
# ...
class MyView(ListView):
def some_method(self, list_of_pks):
model_vals = Document.objects.filter(pk__in = list_of_pks).values()
def perform_action(request):
obj_pk = request.POST.get('obj_pk')
obj = Document.objects.filter(pk = obj_pk)
MySignal.send(sender=Document, instance = obj)
#etc, etc
This works well enough. But my use case calls for different types of Document, one per site, that will have additional fields that aren't known in advance. Based on reading the documentation on abstract base classes, I thought the a reasonable solution would look like:
# models.py for the app
class BaseDocument(models.Model):
contents = models.TextField()
class Meta:
abstract = True
# models.py for a hypothetical site using the app
class SiteDocument(myapp.BaseDocument):
date = models.DateTimeField()
# other site-specific fields
What I don't understand is how to then reference the model in the app's views.py, forms.py, etc. I know BaseDocument.objects.all(), for example, won't return anything since it isn't connected to a database. Conversely, I can't have Document.objects.all() because Document hasn't been created yet and is specific to each site. Is an abstract base class not the correct solution, and if so, what is?
Edit:
It looks like using a OneToOneField may be best suited to my use case, although it looks like that precludes inheriting methods from the superclass and that BaseDocument.objects.all() won't list out all its children.
Alternatively, I was wondering if I could just add a get_document_model() method to my abstract base class, in the style of get_user_model()?
You can't query your abstract classes directly like that since they won't have managers, only the inherited classes. If you really must do inheritance, you can use a concrete base model and inherit from that at the cost of a join on every query.
Think long and hard about whether this is truly necessary, or if you can represent your data in a more generic way. Models make inheritance seem easy, but they're not magic. There are very real performance and complexity considerations.
It might be as easy as adding a type field to your model
class Document(models.Model):
DOCUMENT_TYPES = ['site', 'another', 'whatever']
document_type = models.CharField(choices=DOCUMENT_TYPES)
...
For more information about abstract vs concrete classes and querying, visit How to query abstract-class-based objects in Django?
I ended up going with a solution mentioned in my edit, namely creating a get_document_model() method inspired by get_user_model(). This gives me exactly the desired behavior.
# models.py in app1
from django.db import models
from django.apps import apps as django_apps
class BaseDocument(models.Model):
contents = models.TextField()
class Meta:
abstract = True
def get_document_model():
# exception handling removed for concision's sake
return django_apps.get_model(settings.DOCUMENT_MODEL)
# models.py in app2
from django.db import models
from app1.models import BaseDocument
class SiteDocument(BaseDocument):
date = models.DateTimeField()
Throughout views.py and elsewhere, I changed things that would have been of the form Document.objects.all() to BaseDocument().get_document_model().objects.all().
I would like to change the DB table names on Django1.9. I know we can change the standard issue with "db_table".
Class Foo_Bar:
class Meta:
db_table = "foo_bar"
But I need to write that in every single class and it is tiresome.
I want Django creates a table whose name is lower-cased class name automatically.
But I have no idea how to do.
I even tried to use
self.__class__.name__.lower()
but, I didn't know how Meta works well.
Any advice is helpful.
Thank you.
A very hack-ish method but this code will give you the outer (since you want to get Foo_Bar, not Meta) class name:
import traceback
Class Foo_Bar:
...
class Meta:
db_table = traceback.extract_stack()[-2][2].lower()
Can someone please explain why is meta class used in the following example.
Ex:
Class Employee (models.Model):
name = models.ForeignKey(name)
Gender = models.IntegerField()
class Meta:
ordering = ["Gender"]
Thanks.
Django models use the Meta class to contain extra information about the model that would not necessarily be appropriate to contain within the model class itself. Note that this is not the same as Python's metaclass; that is a completely different topic.
In this case it ordering or sorting the queries to this model by field "Gender"
Because author/programmer wants to sort results by value of Gender field.
In this case it defines the default field for ordering if you don't provide ORDER_BY in your query.
It is explained in Django Documentation for Models
https://docs.djangoproject.com/en/dev/topics/db/models/
Give your model metadata by using an inner class Meta, like:
Class Employee (models.Model):
....
class Meta:
ordering = ["attribute-X"]
Another useful option can be used in class Meta is verbose_name.