change Class property properties (verbose_name exmpl.) from base Django model - python

I have an abstract class with name and slug field.when i inherit from another class i want to change verbose_name parameter according to that class. How can I set this properties? I want to set the verbose_name parameter of the name field to "foo". but I want to use different parameter data for two different classes.
An example:
For ProductModel name field verbose_name="Foo"
For CategoryModel name field verbose_name="Poo"
class BaseProductModel(models.Model):
name = models.CharField(max_length=200)
slug = AutoSlugField(populate_from="name",unique=True,verbose_name="Slug")
class Meta:
abstract=True
class ProductModel(BaseProductModel):
# I want to set the verbose_Name parameter of the name field to "foo".
#The two fields (name and slug) come from the inherited (BaseProductModel) class.
description = models.TextField(max_length=500,verbose_name="Detay")
class CategoryModel(BaseProductModel):
# The two fields (name and slug) come from the inherited (BaseProductModel) class.
# I want to set the verbose_Name parameter of the name field to "Poo".
def __str__(self):
return self.name

You can do this in two ways
First, works only for abstract classes, otherwise it will change verbose_name of parent class too:
class ProductModel(BaseProductModel):
# I want to set the verbose_Name parameter of the name field to "foo".
#The two fields (name and slug) come from the inherited (BaseProductModel) class.
description = models.TextField(max_length=500,verbose_name="Detay")
ProductModel.get_field('name').verbose_name = 'Foo'
And second, do that in the __init__ method:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
field = self._meta.get_field('name')
field.verbose_name = 'Foo'

Related

Change representation of Superclass without field depending of Subclass object (__str__)

I am running a django app and have a setup like this:
ModelSuper(models.Model):
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name
So ModelForeign has a FK to ModelSuper. What happens now is that when I create an instance of ModelForeign I can choose if it belongs either to ModelSub1 or to ModelSub2. But the string representation is ModelSuper Onject (3) where (3) is the id.
Normally I can change this representation by overwriting the __str__ method on the model, but since I do not have any fields on the Supermodel I can't return anything.
What I tried:
I have already implemented the __str__ method in the Submodels but that does not help.
I wanted to make the Super model abstract. But this does not let me point FKs to the Supermodel, so I can't do this. My setup requires this FK
I used a generic FK with django's ContentType framework. This is also not an option because it messes completely with my app and is also not recommended from an SQL perspective.
Also when I do API-calls I get ModelSuper Onject (3) back instead of a human-readable name.
Is there a way to do what I intend to do? Thanks in advance for help and hints. Very much appreciated!
EDIT1: What I tried thanks to Abdul's help:
class ModelA(models.Model):
class Meta:
abstract = False
TYPE_CHOICES = [('sub1', 'sub1'), ('sub2', 'sub2')]
type_model = models.CharField(max_length=50, choices=TYPE_CHOICES, null=True, blank=True)
def __str__(self):
if self.type_model == "sub1":
return "sub1"
elif self.type_model == "sub2":
return "sub2"
else:
return "unkown"
I am not understanding how your foreign key works as model inheritance means the tables are separate. How about trying something like this:-
ModelA(models.Model):
TYPE_CHOICES = [('Sub1', 'ModelSub1'), ('Sub2', 'ModelSub2')]
model_type = models.CharField(max_length=4, choices=TYPE_CHOICES)
def __str__:
# Return string representation using if-else
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name

How can I display all fields from a Many-to-Many Model in Django Admin

I have the following Models:
class ModelA(models.Model):
some_field_A = models.CharField()
some_other_field_A = models.CharField()
class ModelB(models.Model):
some_field_B = models.CharField()
many_to_many_relation = models.ManyToManyField(ModelA)
In admin.py I am using filter_horizontal to edit the ManyToManyField:
class ModelB(admin.ModelAdmin):
model = ModelB
filter_horizontal = ('many_to_many_relation',)
but it shows only some_field_A and I want it to show both fields from ModelA, because the entries in ModelA are unique depending on both fields and as you can see from the picture there are multiple entries with the same value (i.e. some_field_A = EUV) but they have different values for some_other_field_A:
It displays the result of the __str__(…) method you defined in your ModelA, so if you return the value of some_field in the __str__(…) method, then it will return only the data of some_field.
You thus can alter this method and return both fields:
class ModelA(models.Model):
some_field_A = models.CharField()
some_other_field_A = models.CharField()
def __str__(self):
return f'{self.some_field_A} {self.some_other_field_A}'
I'm not sure if this exactly the solution you are looking for but you could override the __str__ method of ModelA to return the information in a single line.
So for example:
class ModelA(models.Model):
first_field = models.CharField(max_length=16)
second_field = models.CharField(max_length=16)
def __str__(self):
return f"{self.first_field} ({self.second_field'})"
Your admin view should then show each object as "foo (bar)"

Parametrize a Python class

Is there any way to parametrize a class in Python? The parametrized class may look something like this:
class FIELDSerializer:
FIELD = serializers.CharField(source='get_FIELD_display', required=False)
class Meta:
model = None
fields = {FIELD}
Which would need to create the following three classes:
class NameSerializer:
name = serializers.CharField(source='get_name_display', required=False)
class Meta:
model = None
fields = {'name'}
class CategorySerializer:
category = serializers.CharField(source='get_category_display', required=False)
class Meta:
model = None
fields = {'category'}
class StateSerializer:
state = serializers.CharField(source='get_state_display', required=False)
class Meta:
model = None
fields = {'state'}
Is this possible or not?
You can do what you want with a factory function, although it's not completely trivial to get the internal variables (attributes) as you want them:
def factory(FIELDname):
class FIELDSerializer:
class Meta:
model = None
fields = {FIELDname}
settatr(FIELDSerializer, FIELDname, serializers.CharField(source=f'get_{FIELDname}_display', required=False))
return FIELDSerializer
CategorySerializer = factory('category')
StateSerializer = factory('state')
NameSerializer = factory('name')
The setattr allows us to set the name of attribute to the FIELDname string. (Thanks to #Code-Apprentice and #juanpa.arrivillaga for this idea.)
I don't know if there's any easy way to avoid the repetition of the field name and the desired class name when you call the factory without using something like exec (which is perfectly legal but usually leaves programmers with a bad taste in their mouths).

How do i turn off django's plural notation in the admin page

I have some tables that are presented as inlines of another class. I have altered the default title of these inline representations by adding an inner class to the respective tables.
class Meta:
verbose_name = 'Binnengekomen punten'
I have only the verbose_name defined but it still adds an s to all the names. So 'Binnengekomen punten' is displayed as 'Binnengekomen puntens'
What i could do is define the plural of verbose_name verbose_name_plural the same as verbose_name. But is there a way to simply turn off the plural notation? I'd love to know thank you.
according to Django docs there isn't method to turn off plural notation.
If verbose_name_plural isn't given Django uses verbose_name + 's'
I’m sure that it’s possible, but can guarantee you that it would not be worth the effort. Just set the verbose_plural_name.
Adding a suffix is hardcoded, we can see this in the source code:
if self.verbose_name_plural is None:
self.verbose_name_plural = format_lazy('{}s', self.verbose_name)
It does not make much sense to write the verbose_name as 'binnengekomen punten' (incoming points), since that is plural and a verbose_name is supposed to be singular. You can however make a decorator that for example automatically defines the plural by adding 'en' to the verbose_name, like:
def add_plural(cls, suffix='en'):
if not hasattr(cls, 'verbose_name_plural'):
try:
cls.verbose_name_plural = cls.verbose_name + suffix
except AttributeError:
pass
return cls
We can then use the decorator like:
class IncomingPoint(models.Model):
# ...
#add_plural
class Meta:
verbose_name = 'Binnengekomen punt'
EDIT: You can turn of capitalization, by wrapping the items in a subclass of string, as is shown is in this ticket #18129:
class NoCap(str):
def upper(self):
return self
def __getitem__(self, key):
return self.__class__(super().__getitem__(key))
We can then wrap the verbose_name and verbose_name_plural into this:
def case_invariant_meta(cls, suffix='en'):
try:
cls.verbose_name = NoCap(cls.verbose_name)
except AttributeError:
pass
try:
cls.verbose_name_plural = NoCap(cls.verbose_name_plural)
except AttributeError:
pass
return cls
We can then annotate the Meta class:
class IncomingPoint(models.Model):
# ...
#case_invariant_meta
#add_plural
class Meta:
verbose_name = 'Binnengekomen punt'

django has more than 1 foreignkey error

My models file works just fine. As soon as I replace every models.Model with MyModel (a child-class of models.Model), one of my models raises a
<class 'puppy.cms.models.Appearance'> has more than 1 ForeignKey to <class 'puppy.cms.models.Segment'>
exception. The only thing that I am doing in the child class is override the clean method.
What could I be doing wrong?
class SansHashUrl(object):
""" Upon each call to clean, iterates over every field,
and deletes all '#/' and '#!/' occurances.
IMPORTANT: This mixin must be listed first in the inheritance list to work
properly. """
def clean(self):
attrs = (field.attname for field in self.__class__._meta.fields
if isinstance(field, models.CharField)
or isinstance(field, models.TextField))
for attr in attrs:
attr_value = self.__getattribute__(attr)
tokens = attr_value.split()
for i, token in enumerate(tokens):
if has_internal_domain(token):
suggested_url = re.sub('#!?/','', token)
tokens[i] = suggested_url
self.__setattr__(attr, ' '.join(tokens))
class MyModel(SansHashUrl, models.Model):
pass
Model that throws the error:
class Appearance(MyModel):
appearance_type = models.CharField(max_length=20,
choices=APPEARANCE_TYPE_CHOICES)
person = models.ForeignKey(Person, related_name='person_appearance')
item = models.ForeignKey(ManagedItem)
class Meta:
unique_together = (('person', 'item'),)
def __unicode__(self):
return self.person.__unicode__()
In reference to:
class Segment(Story, HasStatsTags, HasFullUrl):
...
It might be useful to note that Story is a subclass of ManagedItem (a subclass of MyModel).
You need to declare MyModel (and probably ManagedItem) as an abstract model in its Meta class, otherwise Django will create a separate table for them and define FKs between them.
class MyModel(SansHashUrl, models.Model):
class Meta:
abstract = True

Categories