How to set related_name in ManyToMany field in an abstract model? - python

I have this abstract model:
class HasSystemMessage(models.Model):
class Meta:
abstract = True
messages = models.ManyToManyField(SystemMessage, related_name=?)
I am going to use this abstract model in at least three other models, lets say, A, B, and C. How can I set the related_name dynamically for these classes? for example, for class B, I want the related_name to be Bs. Is it possible to do so?
To further clarify the question, The classes will look like this:
class B(HasSystemMessage):
# Some model fields
class A(HasSystemMessage):
# Some model fields
HasSystemMessage.objects.filter(a__contains=[some elements])

You can use %(class)s or %(app_label)s
class HasSystemMessage(models.Model):
class Meta:
abstract = True
messages = models.ManyToManyField(SystemMessage, related_name=%(app_label)s_%(class)s_related)
From Django docs
Be careful with related_name and related_query_nameĀ¶ If you are using
related_name or related_query_name on a ForeignKey or ManyToManyField,
you must always specify a unique reverse name and query name for the
field. This would normally cause a problem in abstract base classes,
since the fields on this class are included into each of the child
classes, with exactly the same values for the attributes (including
related_name and related_query_name) each time.
To work around this problem, when you are using related_name or
related_query_name in an abstract base class (only), part of the value
should contain '%(app_label)s' and '%(class)s'.
'%(class)s' is replaced by the lower-cased name of the child class
that the field is used in. '%(app_label)s' is replaced by the
lower-cased name of the app the child class is contained within. Each
installed application name must be unique and the model class names
within each app must also be unique, therefore the resulting name will
end up being different.
Ref: https://docs.djangoproject.com/en/2.0/topics/db/models/#be-careful-with-related-name-and-related-query-name

You Just need to put a string in this attribute which specifies the name of the reverse relation from the SystemMessage.Also read in Django Docs
Try this:
class HasSystemMessage(models.Model):
class Meta:
abstract = True
messages = models.ManyToManyField(SystemMessage, related_name='system_message')

Related

create Django ModelAdmin classes dynamically

I have a whole bunch of models of 3 different types (I have 3 abstract base models, let's say Bread, Eggs, and Beer) and lots of models that inherit one of those three).
I would like to register the concrete models with the admin differently based on their abstract base class, without having to write a separate ModelAdmin class for each one (ie I want to write only 3 ModelAdmin classes - one for each abstract base class). Each type of concrete model has a set of fields that I want displayed (they are different on different concrete models, but in a well-defined way, and I can get a list of the fields I want displayed on an Egg model easily). Example:
#abstract base class for Egg
class Egg(models.Model):
fields = ...
def get_only_concrete_fields(self):
'Return all fields defined on a child of Egg that are not defined on Egg'
return concrete_fields
class Meta:
abstract = True
#concrete class
class WhiteEgg(Egg):
morefields = ...
#concrete class
class BrownEgg(Egg):
otherfields = ...
So what I want to do is register both WhiteEgg and BrownEgg in the admin (without having to write a separate modeladmin class for each), and I want to set list_display to the result of get_only_concrete_fields(), plus a few (not all) hand-selected fields from Egg. I know their is a get_list_display() (see https://stackoverflow.com/a/16115125/710394) but I don't think it gets the model, so I can't call get_only_concrete_fields().
I have also tried looping over the models like below, defining list_display for each, and registering each model with a "base" ModelAdmin, but that doesn't work because it just reassigns that modeladmin in each iteration of the loop.
for models in all_egg_models:
modeladmin = MyModelAdmin #or deepcopy(MyModelAdmin) -also doesn't work
fieldnames = ...the fields i want...
setattr(modeladmin, 'list_display', fieldnames)
admin.site.register(model, modeladmin)
I'm stuck - how can I do this, and DRY?
Your idea of overriding the get_list_display method sounds good.
In the method, you can access the model with self.model.

Python Classes Foreign Keys Inheritance

I wonder - is it possible to inherit classes like this?
For exmple, i have 2 abstract classes:
class Book(models.Model):
name = models.TextField()
class Meta:
abstract = True
class Page(models.Model)
num = models.IntegerField()
book = models.ForeignKey('Book')
class Meta:
abstract = True
And so - I want to make inherited classes for these two, let them be BigBook and BigBookPage
But if i do so, python says to me, that my FK field can't have a relation with an abstract model. And i can't find a way to redefine FK in inherited models. So do i have to ONLY create foreign keys in the inherited models - not the parents?
And if i have same model methods, that use foreign keys, defined in the parent models... i have to move them to every child - so they could use their foreign keys?
It sounds like you want to mark Book as proxy=True. See proxy models. It will create a book model, but also let you have other models that inherit from it letting you customize the functionality of the subclasses.
class Book(models.Model):
class Meta:
proxy = True
class BigBook(Book):
# BigBook properties go here.

related_name argument not working as expected in Django model?

I recently got a ForeignKey clash in my Django model. I have the need to have two foreign keys (owner, assigned_to) ultimately pointing to the same model (a user).
From what I understand I need a related_name argument to solve that problem. So I did that:
assigned_to = models.ForeignKey(TaskUser, blank=True, null=True, related_name='user_assignment')
and
owner = models.ForeignKey(TaskUser, related_name="user_ownership"
But I'm still getting an error:
tasks.task: Accessor for field 'owner' clashes with related field 'TaskUser.user
_ownership'. Add a related_name argument to the definition for 'owner'.
tasks.task: Reverse query name for field 'owner' clashes with related field 'TaskUser.user_ownership'. Add a related_name argument to the definition for 'owner'.
Why am I still getting this error?
There is one catch, owner is in a super class (BaseWidget) and assigned_to is in a sub class (Task). Are there issues with using related_name in an inheritance relationship? Do I need to just override the inheritance of owner and redefine related_name in the sub class instead? I'd appreciate any help!
If you have ForeignKey relationships in an abstract base class every class inheriting from it will have this relationship. As a result of this you must not 'hardcode' its related_name, because all sub classes will try to create the same accessor on the realted class (TaskUser in this case).
You should better do something like:
owner = models.ForeignKey(TaskUser, related_name="%(app_label)s_%(class)s_ownership")
See the django docs on this.
If you are using related_name in abstract base class you need to use a '%(app_label)s' and '%(class)s' in it.
Its mentioned in django doc
Be careful with related_name

class definition inside a class?

class ItemForm(djangoforms.ModelForm):
class Meta:
model = Item
exclude = ['added_by']
i can not understand what this piece of code is doing .i understood that ItemForm is inheriting Modelform but then a class definition inside a class ??
The Item class is :
class Item(db.Model):
name = db.StringProperty()
quantity = db.IntegerProperty(default=1)
target_price = db.FloatProperty()
priority = db.StringProperty(default='Medium',choices=[
'High', 'Medium', 'Low'])
entry_time = db.DateTimeProperty(auto_now_add=True)
added_by = db.UserProperty()
It's part of Django's magic. The metaclass for ModelForm (among other classes) looks for an inner Meta class and uses it to make various changes to the outer class. It's one of the deeper parts of Python that most people will never have to deal with first-hand.
In Python you can define classes within other classes as a way of encapsulating the inner class. The way Django is using this is actually quite excellent.
See this link for more info: http://www.geekinterview.com/question_details/64739
Meta is a special class definition.
In this example, it is a simple inheritance model. ModelForm creates a form based on a Model class, so via giving a class definiton to ModelForm class, it creates the form elements according to related Model class definiton.

Can't use an inheriting Django model's Meta class to configure a field defined in an inherited abstract model

I would like to use properties from an inheriting model's Meta class to configure a field defined in an abstract model higher up the inheritance tree:
class NamedModel(models.Model):
class Meta:
abstract = True
verbose_name = 'object'
name = models.CharField("Name",
max_length=200,
db_index=True,
help_text="A meaningful name for this %s." % Meta.verbose_name)
# see what I'm trying to do here?
)
...
class OwnedModel(NamedModel):
class Meta(NamedModel.Meta):
verbose_name = 'owned object'
I would like the help text on the name field of OwnedModel forms to say 'A meaningful name for this owned object'. But it does not: the word 'owned' is missing, which would suggest that the verbose_name from the NamedModel.Meta is used when the model is set up, not OwnedModel.Meta.
This isn't quite what I expect from an inheritance point of view: is there some way to get the field to be created whereby Meta.verbose_name refers to the value on the non-abstract model class, not the abstract one on which the field was defined?
Or am I being daft?
(This may seem like a trivial example, and it is: but it's just to illustrate the point of something more important and complex I am trying to do)
Many thanks in advance.
Why don't you try to make a class.
class BaseNamedModelMeta:
abstract = True
verbose_name = "your text"
And then inherit and override whatever you want like this:
class OwnedModel(NamedModel):
class Meta(BaseNamedModelMeta):
verbose_name = 'owned object'
I think this happens because Meta.verbose_name is used and NamedModel.name is created when class NamedModel is parsed. So later, when class OwnedModel gets parsed, there is no chance to change anything.
Maybe you can set the help_text property on OwnedModel.name later on, but this may change NamedModel.name also.
In similar situations I have put the variable parts in class attribute of the model (not Meta) and then used the by run time methods/properties to generate the texts I need.
In fact I ended up doing the following. The base model gets given a dynamic_field_definition() class method, which can be used to patch up the fields, with the cls argument being the correct (inheriting) class. That means that that cls' Meta attributes are of that correct child, not the original base.
I then wire up that method to get called on the class_prepared signal, so that you know everything's otherwise ready.
class NamedModel(models.Model):
...
#classmethod
def dynamic_field_definition(cls):
pass
def dynamic_field_definition(sender, **kwargs):
if issubclass(sender, NamedModel):
sender.dynamic_field_definition()
class_prepared.connect(dynamic_field_definition)
Then the field properties that vary with model class are simply reconfigured by that class method (or more likely the method as overridden in derived classes).
It's a slightly hacky way to bring a last little bit of OO-ness to Django models, but it works fine for my purpose.

Categories

Resources