how could i get the AbstractClass of a Model in Django?
I tried to do it with type but type gives me the BaseModel class of django or the type of the model itself.
def go_for_the_bases(model):
while hasattr(model, '_meta'):
model = model.__base__
if hasattr(model, '_meta') and model._meta.abstract == True:
yield model
You can user mro() which stands for Method Resolution Order.
ClassName.mro()
which gives the tuple of the class, its base, its base's base, ...., and so on till we reach object.
You can loop over it and check for className._meta.abstract is True till you reach the abstract class.
Related
I can't figure out how and where Django gets the value of the field which value is another serializer. I need to get a place where something like FirstSerializer(data) happens for the following example:
class FirstSerializer(ModelSerializer):
...
class SecondSerializer(ModelSerializer):
first = FirstSerializer()
...
class ThirdSerializer(ModelSerializer):
second = SecondSerializer()
...
class TripleView(APIView):
serializer_class = ThirdSerializer
...
Any thoughts?
I'm trying to change the document 'objects' property to 'query'. It's more intuitive since one is querying the database.
Like;
Collection.query.find()
Instead of;
Collection.objects.find()
I have tried setting a query attribute to my Collection model like;
class Collection(Document):
def __setattr__(self, key, objects):
self.__dict__['query'] = self.objects
But on checking the type it returns a class of the QueryManager instead of Queryset like;
>>>print(type(Collection.query))
<'class' mongoengine.queryset.queryset.QueryManager >
Instead of;
>>>print(type(Collection.query))
<'class' mongoengine.queryset.queryset.Queryset >
Could someone offer a solution ?
Define an abstract Document class and within it define a custom QuerySet manager using queryset_manager wrapper. Inherit the abstract class as a Base class for all other subsequent Document classes.
from mongoengine.document import Document
from mongoengine.queryset import queryset_manager
class BaseDocument(Document):
meta = {'abstract': True}
#queryset_manager
def query(self, queryset):
return queryset
class Foo(BaseDocument):
...
To query use Foo.query.* which is more intuitive
instead of the default Foo.objects.*.
Displaying the type will return <class 'mongoengine.queryset.queryset.Queryset'> as expected.
What I'm trying to do is get a list of all the fields in a serializer which:
Do not contain required=False as a parameter.
Do not come from the parent serializer.
For example, if I have serializers like:
class ParentSerializer(serializers.Serializer):
parent_field = serializers.IntegerField
class ChildSerializer(ParentSerializer):
child_field_required = serializers.IntegerField
child_field_not_required = serializers.IntegerField(required=False)
I'd like the resulting output to be:
['child_field_required']
I've figured out I can get an ordered list of the declared fields like:
self.get_serializer().get_fields()
>> OrderedDict([
('parent_field', IntegerField()),
('child_field_required', IntegerField()),
('child_field_not_required', IntegerField(required=False)),
])
But I haven't been able to move past this step.
As you're inhering from serializers.Serializer, all fields must be declared explicitly on the class body (unlike serializers.ModelSerializer which traverses to the model layer to generate fields automatically). In that case, you can do:
parent_field_names = {
name
for base in type(instance).__bases__
if hasattr(base, '_declared_fields')
for name in base._declared_fields
}
desired_fields = [
(name, field)
for name, field in instance.get_fields().items()
if name not in parent_field_names and
field._kwargs.get('required', None) is not False
]
Assuming instance is a serializers.Serializer instance. parent_field_names refers to a set for O(1) lookups.
The above depends on a couple of implementation details:
The metaclass of serializers.Serializer -- serializers.SerializerMetaclass assigns the declared fields as class attribute _declared_fields dict
Whole constructing (__new__), the serializers.Field class preserves the initially passed keyword arguments as the _kwargs attribute on the instance to the newly created field instance
Notes:
If you want to make this applicable to serializers.ModelSerializer's model fields traversal as well i.e. for non-declared fields or fields overridden in Meta, you need to check for get_fields of each and then get only the fields that are defined in the current serializer
serializers.SerializerMetaclass pops off the fields from class attributes while creating the _declared_fields attribute, so you can't access the fields from class. Otherwise, we could use the attribute check on the base classes to see if any field is defined there, which would result in a shorter code.
I've come up with a solution, but I feel like there is a more elegant one out there...
instance = ChildSerializer()
serializer_fields = instance.get_fields()
parent_serializer_fields = instance.__class__.__bases__[0]().get_fields()
child_fields = {k: serializer_fields[k] for k in set(serializer_fields) - set(parent_serializer_fields)}
required_fields = [k for k, v in child_fields.items() if v.required]
Right now I have the following serializer:
class ShiftSerializer(serializers.ModelSerializer):
confirmed_location = LocationSerializer(required=False)
class Meta:
model = Shift
When I create a new Shift object I can pass along a basic object like this...
{date_requested: "2015-06-11", employee: 4, shift_requested: 2}
...and the object gets created without issue.
In an effort to make Shift objects a bit more verbose about who requested them, I declared a Serializer for the Employee field so that my GETs would include information about the employee:
class ShiftSerializer(serializers.ModelSerializer):
confirmed_location = LocationSerializer(required=False)
employee = EmployeeSerializer(required=False)
class Meta:
model = Shift
That produced the desired effect: now I can see who requested the shift without making a second query to the server for every single user. Unfortunately, after doing that I started getting HTTP 400 errors whenever I tried to POST that same object to make a new Shift:
Invalid data. Expected a dictionary, but got int.
I'm positive the EmployeeSerializer is freaking out because I'm not passing in an entire Employee object, only an id pointing to a particular Employee. Is it possible to include the entire Employee object when GETing Shift objects but still accept just a single integer when POSTing to create a new Shift object?
To get a read-only representation of the employee, you can add a SerializerMethodField. This will leave your employee field intact for POST and PUT requests and add a serialized representation at employee_data.
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
class ShiftSerializer(serializers.ModelSerializer):
confirmed_location = LocationSerializer(required=False)
# this will look for a method named get_employee_data
employee_data = serializers.SerializerMethodField()
class Meta:
model = Shift
fields = (
'confirmed_location',
'date_requested',
'shift_requested',
'employee',
'employee_data',
)
def get_employee_data(self, obj):
""" Returns serialized representation of an employee.
"""
return EmployeeSerializer(obj.employee).data
I have several models connected to each other with ForeignKeys relationships.
The main one in this sort of hierarchy contains a owner field.
I would like to create a single custom manager for all these models that changes the returned queryset depending on the models that is calling it.
I know that manager can access self.model to get the model that it is attached to.
Class Main(models.Model)
owner=models.ForeignKey (User)
owned = OwnedManager()
Class Second(models.Model)
main=models.ForeignKey('Main')
owned = OwnedManager()
Class Third(models.Model)
second=models.ForeignKey('Second')
owned = OwnedManager()
I would like my Custom Manager to have this sort of behavior:
class OwnedManager(models.Manager):
def get_owned_objs(self, owner):
if self.model == 'Main': # WRONG: How do I get the model name?
owned_main = self.filter(owner=owner)
return owned_main
elif self.model == 'Second':
owned_second = self.filter(main__owner=owner)
return owned_second
else:
owned_third = self.filter(second__main__owner=owner)
return owned_third
In order to have a consistent way to call it across different models, like so:
main_object.owned.get_owned_objs(owner=user1) # of the Model Main
second_object.owned.get_owned_objs(owner=user1) # of the Model Second
third_object.owned.get_owned_objs(owner=user1) # of the Model Third
QUESTION:
self.model == 'Main' is wrong. I don't get the model name like this. Is there a way to get it?
Is this efficient? Do you know a better way to implement this? Maybe Custom Managers Inheritance?
EDIT - MY SOLUTION:
The accepted answer below is a good solution but I also found a way to get the model name of the particular model calling the custom manager, that is:
if self.model.__name__ == 'Main':
The key here is the attribute __name__
1) Make abstract model
class AbstractModel(models.Model):
class Meta(models.Meta):
abstract = True
objects = OwnedManager()
2) Inherit your models from AbstractModel, put some key in meta
class Model(AbstractModel)
class Meta(AbstractModel.Meta):
filter_key = 'some_key'
3) Redesign your OwnedManager
class OwnedManager(models.Manager):
def get_owned_objs(self, owner):
if hasattr(self.model._meta, 'filter_key'):
return self.filter(**{self.model._meta.filter_key: owner})
Now you can use SomeModel.objects.get_owned_objs(owner=user1) in any inherited models, where filter_key is setted without getting models's name.