How to Inherit multiple models in odoo 10 - odoo-10

Does anyone know how to inherit multiple models in odoo 10?
I think the below code is right?
class Test(models.Model):
_inherit = ['hr.employee','hr.employee.category']

First the answer of your question is YES. but to inherit multiple class _name property is required .
three types of inheritance is possible in odoo
_inherit = 'model_1'
_inherit = _inherit = ['model_1', 'model_2']
_inherits = {'res.partner': 'partner_id'}
we can inherit model with or without _name
So what about our '_name' property
If the _name as the same value as the inherited class it will do a
basic inheritance.
If you forget to add the _inherit you will redefine the model If
your class _inherit one model and you set a _name different it
will create a new model in a new database table.
If your class inherit many model you have to set _name if your
override an existing model this way you may have some trouble, it
should be avoided. It is better to use this to create new classes that
inherit from abstract model.

you can try with this:
class Test(models.Model):
_name = 'hr.employee'
_inherit = ['hr.employee','hr.employee.category']

Related

Django Rest - Serialize relation with inherited models

I have the following model structure:
class BaseAccount(models.Model):
# some code
class AccountTypeA(BaseAccount):
# some code
class AccountTypeB(BaseAccount):
# some code
Here are my serializers:
class BaseAccountSerializer(serializers.ModelSerializer):
class Meta:
model = BaseAccount
fields = '__all__'
The extended models are defined exactly in the same way.
When I try to serialize the AccountTypeA, I receive back an error stating that the fields from Base account are unknown.
Any idea?
Easy solution to all my problems:
https://pypi.org/project/django-rest-polymorphic/

Serialize available choices and mark selected

I am new to REST and django-rest-framework. I want to get list of available ManyToMany choices along with some way to know which ones are currently selected.
I have model like this:
class PGroup(models.Model):
.
permissions = models.ManyToManyField(
Permission, related_name="group_permissions", help_text=_('Select permissions for this group.')
)
Serializers.
class PermissionSerializer(serializers.ModelSerializer):
class Meta:
model = Permission
fields = ['pk', 'name',]
class PGroupSerializer(serializers.ModelSerializer):
permissions = PermissionSerializer(many=True)
class Meta:
model = PGroup
fields = [....'permissions']
Looking at Browseable API, with this setup I get 'permissions: []'(empty list) for generics.createAPIView and get the associated 'permissions[....]'(non-empty list) for generics.RetrieveUpdateAPIView.
I want a list of available permissions on both API views and also want to know which permissions are already selected for Update API view.
Can anyone please help.
Thanks
There are 2 ways to get the list of choices.
Using the SerializerMethodField,
from rest_framework import serializers
from .models import Permission
class PGroupSerializer(serializers.ModelSerializer):
permissions = PermissionSerializer(many=True)
all_available_permissions = serializers.SerializerMethodField()
def get_all_available_permissions(self, obj):
return Permission.objects.all()
class Meta:
model = PGroup
fields = ['permissions', "all_available_permissions"]
or using source, we can define a custom method on the model and point the serializer to use it using the source argument.
### models.py
class PGroup(models.Model):
.
permissions = models.ManyToManyField(
Permission, related_name="group_permissions", help_text=_('Select permissions for this group.')
)
def all_permissions(self):
return Permission.objects.all()
### serializers.py
class PGroupSerializer(serializers.ModelSerializer):
permissions = PermissionSerializer(many=True)
all_available_permissions = PermissionSerializer(many=True, read_only=True, source="all_permissions")
class Meta:
model = PGroup
fields = ['permissions', "all_available_permissions"]
2nd option is much better, IMO.
Note: you may not always want to send a full list of choices as that could get really slow overtime when u have hundreds or thousands of objects.

How to write a generic CRUD service on django using rest framework?

I am new to django and faced to several problems trying to write a simple service.
What am I trying to do?
I intend to write a generic crud service for my models using rest-framework library.
I don't want to write serializers and views to all my models and trying to optimize code and learn some useful stuff.
My model
Let's imagine I have an abstract BaseBusinessObject
class BaseBusinessObject(models.Model):
CreatedAt = models.DateField()
UpdatedAt = models.DateField()
class Meta:
abstract = True
I also have a plenty of concrete classes, which are inherited from base one:
class Product(BaseBusinessObject):
Description: models.TextField()
Type: models.CharField()
....
class Company(BaseBusinessObject):
Title: models.CharField()
....
class Person(BaseBusinessObject):
Name: models.CharField()
and so on
What I want
I already figured out, that with rest-framework I can create serializers and views, then register router for url .../Product, .../Company, .../Person. But what if I have 1000 classes? This is boring
A. How can I dynamically specified url's for child objects? I don't want to hardcode methods, I am looking for solution...something like this:
.../api/Entities/ClassName
B. How can I then use my dynamically created urls in django-rest-framework?
router.register('persons', PersonViewSet)
How can write it in more generic way?
router.register('<ClassName>', <GenericViewSet>)
C. Using DRF I can create my viewset for each concrete class in my model:
class PersonViewSet(viewsets.ModelViewSet):
queryset = Person.objects.all()
serializer_class = PersonSerializer
But as I said I have a lot of classes. How can I write it in more generic way?
I tried to create a view set for abstract class, but there are some trouble when querying an abstract object.
Is it possible to create such service for an abstract class and then all its child simply(or not simply) inherit CRUD methods?
Maybe I should try to write a factory for serializers and viewsets?
What possible solutions could I implement for solving my problem?
After 2 days of walking around I finally find my solution. May be someone else will face the some problem, so I trying to explain what I had already done.
First, I create a "base" application inside my django project and add it to settings.py. Then I create an abstract class in models.py:
class BaseCrudEntity(models.Model):
pass
class Meta:
abstract = True
I want to write a generic service for CRUD operations for all "business" classes.
The problem is that I don't want to write serializers and views for them - I want to create them "on fly", dynamically. I decided to use django rest framework as well, because I am not intended to create a bycicle again.
I decided to inherit all my "business" classes from that abstract one and write a service for all possible "families"
So I have to create a fabric which is responsible for VeiwSet creation.
Here is my view.py:
class BaseCrudViewSetFabric():
#classmethod
def CreateViewSet(self, _context):
classname = _context.__name__ + 'ViewSet'
return type(classname, (viewsets.ModelViewSet,), {
'queryset':_context.objects.all(),
'serializer_class':BaseCrudSerializerFabric.CreateSrializer(_context)
})
pass
here _context - variable which describes concrete class of my model.
as you can see this function creates a concrete ViewSet based on my context. Inside it a Serializers fabric is called.
Here the code of my serializers.py:
class BaseCrudSerializerFabric():
#classmethod
def CreateSrializer(self, _context):
classname = _context.__name__
_Meta = type('Meta', (), {'model':_context,'fields':'__all__'})
_crudserializer = type(
classname,
(serializers.ModelSerializer,),
{'Meta': _Meta}
)
return _crudserializer
Moreover, I have to write a service for dynamically routing - I don't wanna hardcode my urls.
Here the example ursl.py from core project:
from base.urls import router
url(r'^api/v1/', include(router.urls))
and from base/urls.py:
from rest_framework.routers import DefaultRouter, SimpleRouter
from base.models.BaseCrudEntity import BaseCrudEntity
from base.views.basecrud_view import BaseCrudViewSetFabric
class CustomRouter(SimpleRouter):
def RoutRegister(self):
childs = getChilds(BaseCrudEntity)
#print(childs)
for ch in childs:
if (ch._meta.abstract == False):
#print(ch.__name__)
prefix = ch.__name__
self.register(prefix, BaseCrudViewSetFabric.CreateViewSet(ch))
return(self)
pass
router = CustomRouter()
router.RoutRegister()
Finally I simply create some concrete models:
from django.db import models
from base.models.BaseCrudEntity import BaseCrudEntity
class Person(BaseCrudEntity):
Name = models.CharField(max_length = 255)
Surname = models.CharField(max_length = 255)
Patronymic = models.CharField(max_length = 255, null = True)
DateOfBirth = models.DateField(null = True)
#slug = models.SlugField(default = 'hui', editable = False)
def __str__(self):
return "{} {} {}".format (self.Surname, self.Name, self.Patronymic)
and thats all.
When application starts it register a route for http://127.0.0.1:8000/api/v1/Person and creates serializers and viewsets so all CRUD operations provided by Django- Rest Framework will be provided as well.
I would suggest using query parameters instead of path parameters, then you just register one URL and process the various elements of the request and route it correctly server-side. When do I use path params vs. query params in a RESTful API?

Django Rest Framework - Exclude field from related object

I have two related models and serializers for both of them. When I am serializing one of these models (the serializer has a depth of 1) the result includes some fields from the related object that should't be visible. How an I specify which serializer to use for the relation? Or is there anyway to tell Rest Framework to exclude some fields from the related object?
Thank you,
I think one way would be to create an extra serializer for the model where you want to return only limited number of fields and then use this serializer in the serializer of the other model. Something like this:
class MyModelSerializerLimited(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('field1', 'field2') #fields that you want to display
Then in the other serializer use the MyModelSerializerLimited:
class OtherModelSerializer(serializers.ModelSerializer):
myfield = MyModelSerializerLimited()
class Meta:
model = OtherModel
fields = ('myfield', ...)
depth = 1
You could override restore_fields method on serializer. Here in restore_fields method you can modify list of fields - serializer.fields - pop, push or modify any of the fields.
eg: Field workspace is read_only when action is not 'create'
class MyPostSerializer(ModelSerializer):
def restore_fields(self, data, files):
if (self.context.get('view').action != 'create'):
self.fields.get('workspace').read_only=True
return super(MyPostSerializer, self).restore_fields(data, files)
class Meta:
model = MyPost
fields = ('id', 'name', 'workspace')

ModelSerializer using model property

I'm trying to serialize a model containing a property field that I also want to serialize.
models.py:
class MyModel(models.Model):
name = models.CharField(max_length=100)
slug = models.AutoSlugField(populate_from='name')
#property
def ext_link(self):
return "/".join([settings.EXT_BASE_URL, self.slug])
serializers.py:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('name', 'ext_link')
When trying to get to the related URL, I'm getting a serializer exception (KeyError) on the ext_link property.
How can I serialize the ext_link property?
Because it's not a model field, it needs to be added explicitly to the serializer class
class MyModelSerializer(serializers.ModelSerializer):
ext_link = serializers.Field()
class Meta:
model = MyModel
fields = ('name', 'ext_link')
as #Robert Townley's comment, this work with version 3.8.2:
class MyModelSerializer(serializers.ModelSerializer):
ext_link = serializers.ReadOnlyField()
class Meta:
model = MyModel
fields = "__all__"
The accepted answer doesn't seem to work for me, nor does the ReadOnlyField.
However, I have had success when I use a field that corresponds to the return type of my property function.
So for the example, I would do this:
class MyModelSerializer(serializers.ModelSerializer):
ext_link = serializers.CharField()
class Meta:
model = MyModel
fields = ('name', 'ext_link')
I've been able to do this with ListField, DictField, and IntegerField as well.
Another thing you might want to do is add a property that its contents are not a string. Let's say you have a model called Person and another one called Food that look like this (we assume that each food is the favorite of only one person, making it a OneToMany connection):
class Person(models.Model):
name = models.CharField(max_length=255)
#property
def favorite_foods(self):
return Food.objects.filter(person=self.pk)
class Food(models.Model):
name = models.CharField(max_length=255)
persons_favorite = models.ForeignKey(Person, on_delete=models.CASCADE)
If you want to add favorite_foods in Person's serializer all you have to do is:
class PersonSerializer(serializers.ModelSerializer):
favorite_foods = FoodSerializer(read_only=True, many=True)
class Meta:
model = Person
fields = ('name', 'favorite_foods')

Resources