I'm currently experimenting with model mixins, the idea being to build a small library of small abstract mixin classes defining commonly needed fields.
Here's what i'm currently playing with:
class HtmlAttrsMixin(models.Model):
css_classes = models.CharField(
max_length=256,
verbose_name=_('CSS Classes'),
blank=True,
)
class Meta:
abstract = True
class LinkHtmlAttrsMixin(HtmlAttrsMixin):
target_blank = models.BooleanField(
default=False,
verbose_name=_('Open in a new window /tab'),
)
title = models.CharField(
max_length=512,
verbose_name=_('Title'),
blank=True,
)
class Meta:
abstract = True
class URLMixin(models.Model):
label = models.CharField(
max_length=256,
verbose_name=_('Name'),
blank=True,
)
url = models.CharField(
max_length=4000,
verbose_name=_('URL'),
blank=True,
)
class Meta:
abstract = True
# Concrete model implementing the above mixins:
class TagLine(URLMixin, LinkHtmlAttrsMixin):
enabled = models.BooleanField(_("enabled"), default=True)
def __unicode__(self):
return self.label
This is working fine so far, but there's a little something i don't quite understand.
I'd like to be able to kind of decide of the inherited fields' ordering by simply declaring the mixins in a different order. As far as i know, the default field ordering is based on the order in which those were declared on the model class, and python will resolve attribute names by going through the base classes in the order in which they were listed, so in the above example, i'd expect the css_classes, target_blank & title fields to be listed AFTER label & url in the admin site, and vice versa if i invert the order of the two mixins in the declaration.
But no matter how i list the mixins, the "html_attrs" fields keep appearing first. They are grouped together (which seems logical, since they belong to the same class), but i just can't seem to force the ordering in that way.
I know this is a trivial question - I can just fix that with an admin fieldset, which will prove much more flexible anyway. It just seemed like a convenient trick which i expected to work, so i'm simply interested in understanding why it doesn't.
(Also, if anyone has any advice about model mixins in general, i'm all ears - I've found some info, but not much, and google for django models mixins tends to return lots of results about CBV mixins, which is not what i'm interested in at the moment).
Thanks to anyone who'll care to answer!
This doesn't answer your question, but I do think the approach is really nice.
Reordering fields in Django model
For forms:
How does Django Know the Order to Render Form Fields?
Instead of using a fields attribute in your form to be edited every time you add a new field to your Model class, make a logic to add the exception where you want it.
Another approach: you can use from django.forms import fields_for_model, and create a function to populate your fields attribute using this function. Check the documentation for this method, is really nice!
Related
i have two model classes and they are related by OneToOneField like here
class Book(models.Model):
is_user_ok = models.BooleanFiled()
class Mybook(models.Model):
book = models.OneToOneField( Book, related_name="Book", null=True, blank=True, on_delete=models.SET_NULL)
now i want make a queryset filter by book field property. but here book.is_user_ok
cannot be used. how can i make this queryset filter?
queryset = Mybook.objects.filter(book.is_user_ok=True)
You are looking a common problem, what you really want to do is related_name__column_name in filter, in your case you can try
queryset = Mybook.objects.filter(book__is_user_ok=True=True)
Reference from official docs:
https://docs.djangoproject.com/en/4.0/topics/db/queries/
double underscore is used for joins as you have already set the relationship in model variety of joins work here, it is used as per the model relation.
You are using a wrong syntax. Replace (book.is_user_ok=True) with (book__is_user_ok=True) The problem is, using '.' instead of '__'. Thats the correct syntax when dealing with another model class
I have a model that has 2 separate ManyToManyField relations back to itself
class Company(models.Model):
parent = models.ManyToManyField("self", through='CompanyParent', through_fields=('company_child', 'company_parent'), related_name='+')
child = models.ManyToManyField("self", through='CompanyParent', through_fields=('company_parent', 'company_child'), related_name='+')
The above works fine on my localhost Django v3.0.2/ SQLite 3.8.7.2
To actually publish it, I have had to use Django v2.1.15/ SQLite 3.7.17, but with the published version it is chucking out the following errors
companies.Company.child: (fields.E332) Many-to-many fields with
intermediate tables must not be symmetrical.
companies.Company.parent: (fields.E332) Many-to-many fields with
intermediate tables must not be symmetrical.
companies.Company: (models.E003) The model has two many-to-many
relations through the intermediate model 'companies.CompanyParent'.
What's going on here? Solved the first 2 issues by adding symmetrical=False to each model, but no idea how to solve the final error?
You can check this answer to set up two many-to-many relations.
An example from the mentioned answer:
class Person(models.Model):
name = models.CharField(max_length=127, blank=False)
to_users = models.ManyToManyField(
'self',
symmetrical=False,
related_name='from_users',
through='Event',
through_fields=('from_user', 'to_user'),
)
class Event(models.Model):
item = models.ForeignKey(Item, related_name='events')
from_user = models.ForeignKey(Person, related_name='events_as_giver')
to_user = models.ForeignKey(Person, related_name='events_as_receiver')
In the event anyone happens across the same issue; the above is the answer but thought I would expand slightly after making the necessary changes to my own project
If you are looking to have numerous many-to-many within the same model and if you are on a version of Django after 2.2a1, then the best method is the one detailed in my question; two different models which you can easily call on views or templates; for example -
> data.manytomany1
> data.manytomany2
However, before 2.2a1 you will have issues. For me, this is because on cPanel, I have to use Django v2.1.15 due to the older SQLite 3.7.17 utilised. This means that you can only have one ManyToManyField (see above) and use a filter to get your 2nd manytomany > you will only be able to do this in the views.py
Hope that makes sense
I have written a base class for admin classes for all of my classes that have a field called is_active. It enables the user to see the records of the table along with the is_active field so that they can see which record is active or not and they can edit it.
For most of the classes that I have in my models, there is a field called name so I can easily write 2 lines of the code in admin.py and have a customized admin representation, but the problem is for the classes that don't have a field called name so I have to use a different field in list_display . Since the number of these classes is high, I am looking for a solution to either dynamically get the name of the field that must be in the list_display. Any ideas to solve this problem would be much appreciated.
models.py:
class BaseAdmin(admin.ModelAdmin):
list_display=('id','name','is_active')
list_editable = ('is_active',) # this MUST only contain fields that also are in "list_display"
search_fields=('name',)
class ClassA(models.Model):
name=models.CharField(max_length=20, blank=False, unique=True,)
is_active=models.BooleanField(default=True,)
def __str__(self):
return self.name
class ClassB(models.Model):
my_field=models.CharField(max_length=20, blank=False, unique=True,)
is_active=models.BooleanField(default=True,)
def __str__(self):
return self.my_field
admin.py
class ClassAAdmin(BaseAdmin):
pass
class ClassBAdmin(BaseAdmin):
pass
You are looking for get_list_display https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_list_display
Use that in conjunction with ModelAdmin.model (i.e. self.model inside the get_list_display method) to create a dynamic list display based on the model.
Note that I would personally recommend not baking this automation into the BaseAdmin, and defining what is different between the subclass ModelAdmins (in this case, list_display) separately. It's more explicit and clear. This is pure opinion though.
I'm designing a new Django app and due to several possibilities, I'm not sure which would be the best, thus I'd like opinions, and hopefully improve what I got so far.
This question comes close but not quite. This one touches the flat/nested subject which is helpful, while still not answering the question.
There are many others on the same subject, and yet none tell me what I want to know.
Background
The models have each unique properties with some shared attributes, and I need to reference them in another model, optimally with a single entry point rather than having a field for each possible model.
I want to be able to do complex Django ORM queries involving the Base class and filter by SubClass when needed. E.g Event.objects.all() to return all events. I'm aware of Django model utils Inheritance Manager and intend to use it if possible.
Also, I'll be using django admin to create and manage the objects, so an easy integration is a must. I want to be able to create a new SubEvent directly, without having first to create a Event instance.
Example
To illustrate, let's say I have the following models for app A.
class Event(models.Model):
commom_field = models.BooleanField()
class Meta:
abstract = True
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# and so on
And also an app B, where I want to be able to reference a event which can be of any type, like:
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
# This won't work, because `A.Event` is abstract.
Possible solutions
Use a GenericForeignKey.
# B.models.py
class OtherModel(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
event = GenericForeignKey('content_type', 'object_id')
What I don't like about this is that I'll lose the querying capabilities Django ORM has, and I might need to do additional fiddling to get it working on admin. Not sure, never dealt with this before
Flatten Event
I can bring it all up to the base class and have flags or checks outside the model definition, something like:
class Event(models.Model):
commom_field = models.BooleanField()
email = models.EmailField(blank=True)
title = models.TextField(blank=True)
number = models.IntegerField(default=10)
This might seem like the best idea at first, but of course there are other kind of fields, and that forces me to allow nulls/blanks for most of them (like the email field), losing the db level integrity check.
OneToOne relationships
Rather than abstract like on 1 or flatten on 2 it is possible to have a db table for each, where the models will look like:
class Event(models.Model):
commom_field = models.BooleanField()
class SubEventA(models.Model):
event = models.OneToOneField(Event)
email = models.EmailField(unique=True)
class SubEventB(models.Model):
event = models.OneToOneField(Event)
title = models.TextField(blank=True)
class SubEventC(models.Model):
event = models.OneToOneField(Event)
number = models.IntegerField(default=10)
So far it solved the two initial problems, but now when I get to the admin interface, I'll have to customize each form to create the base Event before saving a SubEvent instance.
Questions
Is there a better approach?
Can any of the choices I present be improved in any direction (ORM query, DB constraints, admin interface)?
I've pondered about both answers and came up with something based off of those suggestions. Thus I'm adding this answer of my own.
I've chosen to use django-polymorphic, quite nice tool suggested by #professorDante. Since this is a multi-table inheritance, #albar's answer is also somewhat correct.
tl;dr
django-polymorphic attends the 3 main requirements:
Allow django ORM querying style
Keep db level constraints by having a multi-table inheritance and one table for each sub class
Easy django admin integration
Longer version
Django-polymorphic allows me to query all different event instances from the base class, like:
# assuming the objects where previously created
>>> Event.objects.all()
[<SubEventA object>, <SubEventB object>, <SubEventC object>]
It also has great django admin integration, allowing seamless objects creation and editing.
The models using django-polymorphic would look like:
# A.models.py
from polymorphic import PolymorphicModel
class Event(PolymorphicModel):
commom_field = models.BooleanField()
# no longer abstract
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# B.models.py
# it doesnt have to be polymorphic to reference polymorphic models
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
Besides, I can reference only the base model from another class and I can assign any of the subclasses directly, such as:
>>> sub_event_b = SubEventB.objects.create(title='what a lovely day')
>>> other_model = OtherModel()
>>> other_model.event = sub_event_b
My .2c on this. Not sure about your design in #3. Each SubEvent subclasses Event, and has a one-to-one to Event? Isn't that the same thing?
Your proposal on the Generic Key is exactly what it is designed for.
Another possibility - Polymorphism with Mixins. Use something like Django-polymorphic, so querying returns you the subclass you want. I use this all the time and its super useful. Then make Mixins for attributes that will be reused across many classes. So a simple example, making an email Mixin
class EmailMixin(models.Model):
email = models.EmailField(unique=True)
class Meta:
abstract = True
Then use it
class MySubEvent(EmailMixin, models.Model):
<do stuff>
This way you dont have redundant attributes on subclasses, as you would if they were all in the parent.
Why not a multi-table inheritance?
class Event(models.Model):
commom_field = models.BooleanField()
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField(blank=True)
class SubEventC(Event):
number = models.IntegerField(default=10)
I'm working on my first real Django project after years of PHP programming, and I am running into a problem with my models. First, I noticed that I was copying and pasting code between the models, and being a diligent OO programmer I decided to make a parent class that the other models could inherit from:
class Common(model.Model):
name = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.name
class Meta:
abstract=True
So far so good. Now all my other models extend "Common" and have names and dates like I want. However, I have a class for "Categories" were the name has to be unique. I assume there should be a relatively simple way for me to access the name attribute from Common and make it unique. However, the different methods I have tried to use have all failed. For example:
class Category(Common):
def __init__(self, *args, **kwargs):
self.name.unique=True
Causes the Django admin page to spit up the error "Caught an exception while rendering: 'Category' object has no attribute 'name'
Can someone point me in the right direction?
No, Django doesn't allow that.
See the docs: http://docs.djangoproject.com/en/1.1/topics/db/models/#field-name-hiding-is-not-permitted
Also answered in other questions like: In Django - Model Inheritance - Does it allow you to override a parent model's attribute?
You have a small mistake in your Common class
class Common(model.Model):
self.name = models.CharField(max_length=255)
should be
class Common(model.Model):
name = models.CharField(max_length=255)
Note that UNIQUE constraint in fact has nothing to do with Django, so you're free to add it in your database table. You can also use post-syncdb hook for that purpose.
Try using Meta.unique_together to force it into its own unique index. Failing that, it's probably easiest to create two separate abstract classes, one with the field unique and one not.