Django: Overriding 'objects' of superclass without changing the code in superclass - django

I have 2 classes defined like:
class Parent(models.Model)
# class definition
And the second class:
class Child(models.Manager):
def get_queryset(self):
pass
Now I want to override the 'objects' of class Parent. Normally it will go like this:
class Parent():
objects = Child()
But I can't modify class Parent because it's a third party library.
Is there any workaround for this problem?

setattr(Parent, 'objects', Child())
write this line in end of file where you have your manager class.
this will override the objects attribute of Parent Class if exist or add objects attribute.
Example:
from django.db import models
class abc(models.Model):
pass
class pqr(models.Manager):
def get_queryset(self, *args, **kwargs):
print('in get_queryset')
setattr(abc, 'objects', pqr())
now in shell get or create object of parent class in this example abc class and perform any operation on in .
a = abc()
a.objects.all()
but this method is wrong it will override the objects attribute and if you want to use this method then use attribute name different than objects

Related

Django Rest Framework: serializer for two models that inherit from a common base abstract model

There is an abstract model that defines an interface for two child models.
I've been asked to create an API endpoint that will return instances from those child models (including only the common fields defined thanks to the interface father class).
The problem raises when defining the Serializer.Meta.model attribute.
Anyway, code is always clearer:
models.py
class Children(Model):
class Meta:
abstract = True
def get_foo(self):
raise NotImplementedError
class Daughter(Children):
def get_foo(self):
return self.xx
class Son(Children):
def get_foo(self):
return self.yy
api/views.py
class ChildrenApiView(ListAPIView):
serializer_class = ChildrenSerializer
def get_queryset(self):
daughters = Daughter.objects.all()
sons = Son.objects.all()
return list(daughters) + list(sons)
serializers.py
class ChildrenSerializer(ModelSerializer):
foo = CharField(source="get_foo", read_only=True)
class Meta:
model = Children # <========= HERE IS THE PROBLEM
fields = ('foo',)
Some thoughts;
I know I'm not able to point out to the abstract model Children (wrote it for showing the inntention)
I tried to leave ChildrenSerializer.Meta.model empty
Seems that I can choose whichever Daughter or Son but not sure if that solution has any side-effect or is the way to go.
Tried to create DaughterSerializer & SonSerializer and use the method get_serializer_class(self) at the view, but wasn't able to make it run
I would probabaly not have a model serializer, and instead have a standard Serializer, with all the fields that you want to return in the view.
This will make it applicable for both Son and Daughter.
So the serializer would be something like:
class ChildrenSerializer(serializers.Serializer):
foo = CharField(source="get_foo", read_only=True)

Extend Behavior of models.Model

In django, I am trying to extend the behavior of the models.Model class.
I want to execute code as the model inits, as well as using the default init.
Here is the code I so far which looks like what im wanting to do but the behavior is incorrect.
class DirtyModel(models.Model):
def __init__(self, *args, **kwargs):
super(DirtyModel, self).__init__(*args, **kwargs)
print("extended")
class Foo(DirtyModel):
bar = models.TextField()
This code tries to make a model called DirtyModel, which I understand why, but I don't suppose I know how to extend the Model class otherwise.
How should I go about creating a custom models.Model class to use in my models?
How should I go about creating a custom models.Model class to use in my models?
Based on your comments, you likely want to create an abstract superclass:
class DirtyModel(models.Model):
def __init__(self, *args, **kwargs):
super(DirtyModel, self).__init__(*args, **kwargs)
print("extended")
class Meta:
abstract = True
By not setting abstract = True, Django will create a DirtyModel as well as a Foo model here, that has an (implicit) OneToOneField to DirtyModel.
If you make it abstract, you basically say that one can not construct a DirtyModel itself, but only some non-abstract subclasses.

Django - add model field validator in __init__

I need to add a field validator through an abstract class.
The code I'm working with supposes that every class inheriting from this abstract class has a field name, but this field itself isn't defined in the abstract class unfortunately.
So I tried the following, but I'm not sure what is the good way of finding the field in self._meta.fields, since this is a list..??
class AbsClass(models.Model):
class Meta:
abstract = True
def __init__(self, *args, **kw):
super(AbsClass, self).__init__(*args, **kw)
name_field = [f for f in self._meta.fields if f.name == 'name'][0] # Is there another way?
name_field.validators.append(my_validator)
You need Options.get_field:
...
name_field = self._meta.get_field('name')
name_field.validators.append(my_validator)
...
Your approach, however, doesn't seem like a good idea: you model's field instances are shared between all instances of your model (their references are stored in a class attribute, not in instance attributes). This means that every time you instantiate an object of your model, you'll be adding another copy of my_validator to the field's validators, because you're adding it to the same field instance.
You could implement a metaclass for your abstract base class and add the validator at compile time instead of tampering with field instances at runtime, something along the lines of this (not tested):
from django.utils.six import with_metaclass
from django.db.models.base import ModelBase
# inherit from Django's model metaclass
class AbsClassMeta(ModelBase):
def __new__(cls, name, bases, attrs):
if 'name' in attrs:
attrs['name'].validators.append(my_validator)
elif name != 'AbsClass':
# is it an error to not have a "name" field in your subclasses?
# handle situation appropriately
return super(AbsClassMeta, cls).__new__(cls, name, bases, attrs)
class AbsClass(with_metaclass(AbsClassMeta, models.Model)):
...
Note that your AbsClass class itself will also be created using this metaclass. If you decide to throw an exception in AbsClassMeta.__new__ if the class doesn't have a name field, you need to take this into account, since your AbsClass class doesn't have a name field.
In Django 2.x
instead of
name_field = self._meta.get_field('name')
name_field.validators.append(my_validator)
you can do:
name_field = self.fields['name']
name_field.validators.append(my_validator)

Interface like behavior with django models - Overriding models.Field

I am trying to design an abstract model that contains a field. Subclassed models will have this field, but they will be of various field types.
Example
class AbsModel(models.Model):
data = models.??? #I want subclasses to choose this
def __unicode__(self):
return data.__str__()
class Meta:
abstract = True
class TimeModel(AbsModel):
data = models.TimeField()
...
class CharModel(AbsModel):
data = models.CharField(...)
...
I am looking for a way to enforce the existence of the data field so I can write unicode once for all objects.
If this isn't possible, how can I refer to the "data" field of the subclass when calling the super class's unicode
I have a feeling this second question has an obvious answer I am missing.
It's not possible to override a superclass field where the field is of type models.Field.
https://docs.djangoproject.com/en/1.4/topics/db/models/#field-name-hiding-is-not-permitted
You can get round this by defining a field of another type in the superclass, and then overriding it in the child (perhaps include a __str__() method just in case the data field isn't overriden).
from django.db import models
class AbsDataField:
def __str__(self):
return "undefined"
class AbsModel(models.Model):
data = AbsDataField
def __unicode__(self):
return self.data.__str__()
class Meta:
abstract = True
class TimeModel(AbsModel):
data = models.TimeField()
#...
class CharModel(AbsModel):
data = models.CharField(max_length=32)
#...
You can write something like that:
class AbsModel(models.Model):
def __unicode__(self):
if hasattr(self, "data") and isinstance(self.data, models.Field):
return data.__str__()
return u"Unknown"
You cannot do that in Django:
In normal Python class inheritance, it is permissible for a child
class to override any attribute from the parent class. In Django, this
is not permitted for attributes that are Field instances (at least,
not at the moment). If a base class has a field called author, you
cannot create another model field called author in any class that
inherits from that base class.
Overriding fields in a parent model leads to difficulties in areas
such as initializing new instances (specifying which field is being
initialized in Model.__init__) and serialization. These are features
which normal Python class inheritance doesn't have to deal with in
quite the same way, so the difference between Django model inheritance
and Python class inheritance isn't arbitrary.
[...]
Django will raise a FieldError if you override any model field in any
ancestor model.

Django Problem inheriting formfield_callback in ModelForms

I've only been using Django for a couple of weeks now, so I may be approaching this all kinds of wrong, but:
I have a base ModelForm that I put some boilerplate stuff in to keep things as DRY as possible, and all of my actual ModelForms just subclass that base form. This is working great for error_css_class = 'error' and required_css_class = 'required' but formfield_callback = add_css_classes isn't working like I would expect it to.
forms.py
# snippet I found
def add_css_classes(f, **kwargs):
field = f.formfield(**kwargs)
if field and 'class' not in field.widget.attrs:
field.widget.attrs['class'] = '%s' % field.__class__.__name__.lower()
return field
class BaseForm(forms.ModelForm):
formfield_callback = add_css_classes # not working
error_css_class = 'error'
required_css_class = 'required'
class Meta:
pass
class TimeLogForm(BaseForm):
# I want the next line to be in the parent class
# formfield_callback = add_css_classes
class Meta(BaseForm.Meta):
model = TimeLog
The end goal is to slap some jquery datetime pickers on forms with a class of datefield/timefield/datetimefield. I want all of the date time fields within the app to use the same widget, so I opted to do it this way than explicitly doing it for each field in every model. Adding an extra line to each form class isn't that big of a deal, but it just bugged me that I couldn't figure it out. Digging around in the django source showed this is probably doing something I'm not understanding:
django.forms.models
class ModelFormMetaclass(type):
def __new__(cls, name, bases, attrs):
formfield_callback = attrs.pop('formfield_callback', None)
But I don't know how __init__ and __new__ are all intermangled. In BaseForm I tried overriding __init__ and setting formfield_callback before and after the call to super, but I'm guessing it needs to be somewhere in args or kwargs.
__new__ is called before object construction. Actually this is a factory method that returns the instance of a newly constructed object.
So there there are 3 key lines in ModelFormMetaclass:
formfield_callback = attrs.pop('formfield_callback', None) #1
fields = fields_for_model(opts.model, opts.fields,
opts.exclude, opts.widgets, formfield_callback) #2
new_class.base_fields = fields #3
In the class we attach base_fields to our form.
Now let's look to ModelForm class:
class ModelForm(BaseModelForm):
__metaclass__ = ModelFormMetaclass
This means that ModelFormMetaclass.__new__(...) will be called when we create a ModelForm instance to change the structure of the future instance. And attrs of __new__ (def __new__(cls, name, bases, attrs)) in ModelFormMetaclass is a dict of all attributes of ModelForm class.
So decision is to create new InheritedFormMetaclass for our case (inheriting it from ModelFormMetaclass). Don't forget to call new of the parent in InheritedFormMetaclass. Then create our BaseForm class and say:
__metaclass__ = InheritedFormMetaclass
In __new__(...) implementation of InheritedFormMetaclass we could do all we want.
If my answer is not detailed enough please let me know with help of comments.
You may set widgets class like this:
class TimeLogForm(BaseForm):
# I want the next line to be in the parent class
# formfield_callback = add_css_classes
class Meta(BaseForm.Meta):
model = TimeLog
widgets = {
'some_fields' : SomeWidgets(attrs={'class' : 'myclass'})
}
For what you're trying to accomplish, I think you're better off just looping through the fields on form init. For example,
class BaseForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BaseForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs['class'] = 'error'
Clearly you'll need a little more logic for your specific case. If you want to use the approach that sergzach suggested (overkill for your particular problem I think), here's some code for you that will call formfield_callback on the base class in the case the subclass doesn't define it.
baseform_formfield_callback(field):
# do some stuff
return field.formfield()
class BaseModelFormMetaclass(forms.models.ModelFormMetaclass):
def __new__(cls, name, bases, attrs):
if not attrs.has_key('formfield_callback'):
attrs['formfield_callback'] = baseform_formfield_callback
new_class = super(BaseModelFormMetaclass, cls).__new__(
cls, name, bases, attrs)
return new_class
class BaseModelForm(forms.ModelForm):
__metaclass__ = OrganizationModelFormMetaclass
# other form stuff
Finally, you might wanna look into crispy forms: https://github.com/maraujop/django-crispy-forms
sergzach is correct that you have to use metaclasses; overriding __init__ is not enough. The reason is that the metaclass for ModelForm (which will be called for all ModelForm subclasses unless you specify another metaclass in a subclass) takes the class definition, and using the values in the class definition creates a class with class attributes. For example, both META.fields and our formfield_callback is used to create form Fields with various option (like which widget).
That means AFAIU formfield_callback is a parameter to the metaclass used when creating your custom model form class, not some value used at runtime when actual form instances are created. That makes placing formfield_callback in __init__ useless.
I solved a similiar problem with a custom metaclass like
from django.forms.models import ModelFormMetaclass
class MyModelFormMetaclass(ModelFormMetaclass):
def __new__(cls,name,bases,attrs):
attrs['formfield_callback']=my_callback_function
return super(MyModelFormMetaclass,cls).__new__(cls,name,bases,attrs)
and in the base class for all my model forms setting the metaclass
class MyBaseModelForm(ModelForm):
__metaclass__=MyModelFormMetaclass
...
which can be used like (at least in Django 1.6)
class MyConcreteModelForm(MyBaseModelForm):
# no need setting formfield_callback here
...