DJANGO - class Meta / varibales db_table - django

I would like to get a variable in my meta class
class Meta:
abstract = True
db_table = 'bikes_table'
managed = False
I want to get a variable from self like above :
class Meta:
abstract = True
db_table = 'bikes_table' if self.customers=True else 'cars_table'
managed = False
Is it possible de get variable ?
Thank you

As stated in the comments, the surface problem is that "self" won't be available at class definition time, of course.
Some of the fields in the Metadata could have values that could change at runtime, for each instance, those will accept callback functions instead of static values (but looking at the documentation, there seems to be none)
Now, the table corresponding to a certain Model is used by the ORM and will be bound at the class level. Doubly so because a lot of the queries dealing with a class have to issue database statements without having an instance, as is the case for any query. I mean, in your example, a vehicle.objects.all() would have to query both database tables and bring the results together in the same response.
It could be done by subclassing Django's internal Queryset databases, and carefully rewriting it to look at the "table" model meta data in a custom way, but it would be prone to error.
So, you'd rather rethink your design. It is easier to simply have a Vehicle base class, just use "Vehicle" on your forms and relations, and derive the Bicycle and Car classes from that - either a car or a bicycle could be used wherever a vehicle can. Django provides support for the relationship itself with Generic Relations

Related

Different Field types of model class

I'm trying to provide for users possibility to store in SomeModel a list of ExtraParameters of any number or kind (it may be something small like IntegerField, BooleanField or quite large like TextField).
I tried to implement ExtraParameter abstract model class, that will keep ForeignKey to SomeModel, and also its child classes with only one parameter like:
class ExtraParameter(models.Model):
somemodel = models.ForeignKey(SomeModel, ...)
class Meta:
abstract = True
class IntegerExtraParameter(ExtraParameter):
value = models.IntegerField()
I believe it takes multiple small classes like this one so it could be migrated to multiple database tables of different fields.
Am I right? Please provide better solution. Maybe other way to decorate ExtraParameter is possible?
The problem with this approach is while implementing template it is not so easy to get all the stored parameters of all kind by doing somemodel.extraparameters.all(), rather I need to call every child class explicitly and build set from it. But also I've seen a solution with finding all subclasses of any class inside app's config, so it would help.
Jakub
I think the answer you are looking for is covered in great depth in here:
https://stackoverflow.com/a/7934577/
Personally I like the way Django and JsonField works together.
https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.JSONField
JsonField would give you vast flexibility with dynamic metadata.
In this example I'm simply attaching a JsonField to those models that are in need of dynamic meta data. extra_information could then hold data about what kind of values is in relation with the parent model.
class ExtraInformationModel(models.Model):
# Abstract model that adds json field for additional meta data
extra_information = JSONField()
class Meta:
abstract = True
class SomeModel(ExtraInformationModel):
# extra_information field comes from the abstract model
...usual model stuff..
Making queries with JsonFields is as easy as using Django orm usually is.
https://docs.djangoproject.com/en/3.1/topics/db/queries/#querying-jsonfield
(example from Django documentation)
Dog.objects.create(name='Rufus', data={
'breed': 'labrador',
'owner': {
'name': 'Bob',
'other_pets': [{
'name': 'Fishy',
}],
},
})
Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
Dog.objects.filter(data__breed='collie')

Transient use of Django Model instances

I have a use case where a particular class can either be transient or persistent. Transient instances are build from a JSON payload on a PUT call, and may either be persisted to the database or used during the server call and then either returned or discarded. What is best practice for this case? My options seem to be:
Write two classes, one of which is a models.Model subclass, and the other of which isn't, and make them implement the same API, or
Use the Model subclass, but be careful not to call save().
Is either of these preferable, according to conventional use of Django models?
You'll need both:
abstract = True is useful if inheritants still should be concrete models, so that no table should be created just for the parent class. It allows you to opt out of multi-table inheritance, and instead have the shared attributes duplicated to inheritants tables instead (abstract base inheritance).
managed = False is useful if the inheriting class should never be persisted at all. Django migrations and fixtures won't generate any database table for this.
class TransientModel(models.Model):
"""Inherit from this class to use django constructors and serialization but no database management"""
def save(*args, **kwargs):
pass # avoid exceptions if called
class Meta:
abstract = True # no table for this class
managed = False # no database management
class Brutto(TransientModel):
"""This is not persisted. No table app_brutto"""
#do more things here
pass
In order to remain as DRY as possible, you could have an abstract mock class deriving your model:
class A(models.Model):
# fields'n'stuff
class TransientA(A):
def save(*args, **kwargs):
pass # avoid exceptions if called
class Meta:
abstract = True # no table created
Now, even if you call save on it anywhere (even in methods inherited from A), you'll be shooting blanks.

Django how to design multiple variants of a model

Say you're a publisher and have multiple types of article.
Standard article, sponsored article, and review
And sponsored article has some sponsored-only fields, say sponsor and promotion_end_date
And review has some other review only fields, like product return address
How would you design this?
I've come across this problem a few times, and I always end up doing it in one model with all the fields available but not required.
But it just feels bad because it leaves room for mistakes with the administrators. What if they fill in product return address but its not a review? etc.
And most of the time you want it in one model, to query the very similar objects together since 90% of the fieldset is the same between them. ANd its very helpful for things like finding most popular
EDIT:
These models will almost always be queried together. So it makes no sense to put them in different tables and then work around that.
They should be in the same table and indexed.
Otherwise every single request would be doing 3x queries, not one. And then having to merge and sort the queries computationally afterwards.
You can always have one base model which can be inherited by other subsequent models that you named. In your case.
class BaseArticle(models.Model):
class Meta:
abstract = True
fields that are common to the models that will be inheriting this
class StandardArticle(BaseArticle):
fields specific to StandardArticle
You can have your other models in same fashion as StandardArticle
Use abstract base classes:
class ArticleBase(models.Model):
# fields
class Meta:
abstract = True
class Article(ArticleBase):
pass
class SponsoredArticle(ArticleBase):
# additional fields
class Review(ArticleBase):
# additional fields
Or multi-table inheritance:
class Article(models.Model):
# fields
class SponsoredArticle(Article):
# additional fields
class Review(Article):
# additional fields
In the latter case you can query all articles with Article.objects.select_related('sponsoredarticle', 'review').all() (you may create custom manager to avoid typing select_related(...) each time). select_related() is necessary to avoid DB query when accessing child class, e. g. aricle.review.

Building a list of ModelForms based on relation

I think this is best explained with some simple model code (I'm writing this from scratch so possible syntax issues - unimportant here):
class Car(models.Model)
make = models.CharField(...)
model = models.CharField(...)
class StatisticType(models.Model):
name = models.CharField(...)
class Statistic(models.Model)
car = models.ForeignKey('Car')
stype = models.ForeignKey('StatisticType')
data = models.CharField(...)
class Meta:
unique_together = (('car', 'stype'),)
We have a car with some hard-coded stats and we have some database controlled statistics. I might add Colours, Wheel Size, etc. The point is it's editable from the admin so neither I or the client need to climb through the data, but it's limited so users can only pick one of each stat (you can't define "Colours" twice).
So I'm trying to write the data input form for this now and I want a list of optional ModelForms that I can chuck on the page. I've got the simplest ModelForm possible:
class StatisticForm(forms.ModelForm):
class Meta:
model = Statistic
The tricky part (in my head) is generating an instance of this ModelForm for each StatisticType, regardless of it existing yet. That is to say if a Car object doesn't have a Colour assigned to it, the form still shows. Similarly, if it does, that instance of a Statistic is loaded in the ModelForm.
In my view, how do I generate a list of these things, regardless of there being a pre-existing instance of any given Statistic?
This seems like it should be a stupidly simple thing to do but it's late on Friday and everything looks skwonky.
Sounds like you might want to leverage an inline model formset factory.
That would allow you to create as many instances of your Statistic object as you need. If you're needing to create instances of your StatisticType on the fly, that's a bit different.
When Django instantiates forms, for a foreign key, m2m or choice field, it will only accept choices that it deems "valid", and will complain if you add a choice using JavaScript that doesn't exist in a related model or set of choices server-side.
So, if you need to make StatisticTypes on the fly, and then populate formset instances with this new value, I would suggest using Knockout.js. It's very good at keeping lots of DOM elements in sync when data changes.

Django Model Field for Abstract Base Class

I've searched around stack overflow for an answer to this (probably simple) question, but most of the solutions I see seem overly complicated and hard to understand.
I have a model "Post" which is an abstract base class. Models "Announcement" and "Event" inherit from Post.
Right now I'm keeping related lists of Events and Announcements in other models. For instance, I have "removed_events" and "removed_announcements" fields in another model.
However, in my project, "removed_events" and "removed_announcements" are treated exactly the same way. There is no need to disambiguate between a "removed event" and a "removed announcement." In other words, a field keeping track of "removed_posts" would be sufficient.
I don't know how to (or perhaps can't) create a field "removed_posts," since Post is abstract. However, right now I feel like I'm repeating myself in the code (and having to do a lot of clutter-some checks to figure out whether the post I'm looking at is an event or an announcement and add it to the appropriate removed field).
What is the best option here? I could make Posts non-abstract, but Post objects themselves should never be created, and I don't think I can enforce this on a non-abstract object.
My understanding of databases is weak, but I'm under the impression that making Post non-abstract would complicate the database due to joins. Is this a big deal?
Finally, there are other fields in other models where I'd like to condense things that amount to an event_list and an announcement_list into a post_list, but those fields do need to be disambiguated. I could filter the post_list based on post type, but the call to filter() would be slower than being able to directly access the event and announcement lists separately, wouldn't it? Any suggestions here?
Thanks a ton for reading through this.
There are two kinds of model subclassing in Django - Abstract Base Classes; and Multi-Table inheritance.
Abstract Base Classes aren't ever used by themselves, and do not have a database table or any form of identification. They are simply a way of shortening code, by grouping sets of common fields in code, not in the database.
For example:
class Address(models.Model):
street = ...
city = ...
class Meta:
abstract = True
class Employee(Address):
name = ...
class Employer(Address):
employees = ...
company_name = ...
This is a contrived example, but as you can see, an Employee isn't an Address, and neither is an Employer. They just both contain fields relating to an address. There are only two tables in this example; Employee, and Employer - and both of them contain all the fields of Address. An employer address can not be compared to an employee address at the database level - an address doesn't have a key of its own.
Now, with multi-table inheritance, (remove the abstract=True from Address), Address does have a table all to itself. This will result in 3 distinct tables; Address, Employer, and Employee. Both Employer and Employee will have a unique foreign key (OneToOneField) back to Address.
You can now refer to an Address without worrying about what type of address it is.
for address in Address.objects.all():
try:
print address.employer
except Employer.DoesNotExist: # must have been an employee
print address.employee
Each address will have its own primary key, which means it can be saved in a fourth table on its own:
class FakeAddresses(models.Model):
address = models.ForeignKey(Address)
note = ...
Multi-table Inheritance is what you're after, if you need to work with objects of type Post without worrying about what type of Post it is. There will be an overhead of a join if accessing any of the Post fields from the subclass; but the overhead will be minimal. It is a unique index join, which should be incredibly quick.
Just make sure, that if you need access to the Post, that you use select_related on the queryset.
Events.objects.select_related(depth=1)
That will avoid additional queries to fetch the parent data, but will result in the join occurring. So only use select related if you need the Post.
Two final notes; if a Post can be both an Announcement AND an Event, then you need to do the traditional thing, and link to Post via a ForeignKey. No subclassing will work in this case.
The last thing is that if the joins are performance critical between the parent and the children, you should use abstract inheritance; and use Generic Relations to refer to the abstract Posts from a table that is much less performance critical.
Generic Relations essentially store data like this:
class GenericRelation(models.Model):
model = ...
model_key = ...
DeletedPosts(models.Model):
post = models.ForeignKey(GenericRelation)
That will be a lot more complicated to join in SQL (django helps you with that), but it will also be less performant than a simple OneToOne join. You should only need to go down this route if the OneToOne joins are severely harming performance of your application which is probably unlikely.
Generic relationships and foreign keys are your friend in your path to succeed. Define an intermediate model where one side is generic, then the other side will get a related list of polymorphic models. It's just a little more complicated than a standard m2m join model, in that the generic side has two columns, one to ContentType (actually a FK) and the other to the PK of the actual linked model instance. You can also restrict the models to be linked with using standard FK parameters.
You'll get used with it quickly.
(now that I get an actual keyboard to write with, here there is the example:)
class Post(models.Model):
class Meta: abstract = True
CONCRETE_CLASSES = ('announcement', 'event',)
removed_from = generic.GenericRelation('OwnerRemovedPost',
content_type_field='content_type',
object_id_field='post_id',
)
class Announcement(Post): pass
class Event(Post): pass
class Owner(models.Model):
# non-polymorphic m2m
added_events = models.ManyToManyField(Event, null=True)
# polymorphic m2m-like property
def removed_posts(self):
# can't use ManyToManyField with through.
# can't return a QuerySet b/c it would be a union.
return [i.post for i in self.removed_post_items.all()]
def removed_events(self):
# using Post's GenericRelation
return Event.objects.filter(removed_from__owner=self)
class OwnerRemovedPost(models.Model):
content_type = models.ForeignKey(ContentType,
limit_choices_to={'name__in': Post.CONCRETE_CLASSES},
)
post_id = models.PositiveIntegerField()
post = generic.GenericForeignKey('content_type', 'post_id')
owner = models.ForeignKey(Owner, related_name='removed_post_items')
class Meta:
unique_together = (('content_type', 'post_id'),) # to fake FK constraint
You can't filter into the related collection like a classic many-to-many, but with the proper methods in Owner, and using the concrete classes' managers smartly, you get everywhere you want.