Django serializer call a function - django

I have:
class ProfileSerializer(serializers.ModelSerializer):
relevence = ??
The thing is I want to call relevance as a separate function.
I do not want to write it as a serializer method field nor under a model, can it be done?

from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
days_since_joined = serializers.SerializerMethodField()
class Meta:
model = User
def get_days_since_joined(self, obj):
return (now() - obj.date_joined).days

Ok. do it like
class ProfileSerializer(serializers.ModelSerializer):
relevence = min(1,2,3,4) #user inbuilt `min`
But the thing is, function will execute one time only. That means when defining of class happens.

Related

Serialise an extended User Model in Django

I'm extending a django auth user model in a Profile model:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
activity = models.IntegerField(default=500)
def _str_(self):
return self
in my views I'm getting the current auth user and I get the associated profile:
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getUserProfile(request):
profile = Profile.objects.get(user = request.user)
serializer = profileSerializer(profile, many=False)
return Response(serializer.data)
Here is my serializers code:
from rest_framework import serializers
from .models import Profile
class profileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('first_name', 'activity')
The error I'm getting that Profie object has not a first_name attribute, but when I replace 'first_name' with 'user' I get only the id of the user, so I want to show the first_name as well.
Thank you
The OneToOneField connects your field user in the Profile model to a User object which has a first_name attribute, but in the Profile table that user field is just a number that says which User object it is. To access the first name, you can do this:
from rest_framework import serializers
from .models import Profile
class profileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
Then in your template you could display the first_name like this, assuming you pass person to it where person is an instance of the Profile model:
{{ person.user.first_name }}
I also think it would be less confusing to use another name for the field, rather then user. Call it person maybe, like:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
person = models.OneToOneField(User, on_delete=models.CASCADE)
activity = models.IntegerField(default=500)
def _str_(self):
return self
Try this:
from django.contrib.auth.models import User
class profileSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(max_length=200, read_only=True)
class Meta:
model = Profile
fields = ('user', 'first_name', 'activity')
def get_first_name(self, obj):
return User.objects.filter(id=obj.user.id).first().values_list('first_name', flat=True).last()
I was able to show the fields I wanted using Django depth in the serializer
from rest_framework import serializers
from .models import Profile
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('first_name', 'last_name')
class profileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields = ('activity','user')
depth=1

Import cycle with Model, ModelManager and ModelSerializer

I am using Django with Django Rest Framework for serializers.
I have the following situation.
In file models.py:
from django.db.models import Manager, Model, CharField
from .serializers import MyModelSerializer
class MyModelManager(Manager):
serializer_class = MyModelSerializer
class MyModel(Model):
name = CharField(max_length=64)
objects = MyModelManager()
In file serializers.py:
from rest_framework.serializers import ModelSerializer
from models import MyModel
class MyModelSerializer(ModelSerializer):
class Meta:
model = MyModel
fields = ('name',)
However, this leads to an import cycle, since both files try to import each other. I could prevent this by making a local import:
class MyModelManager(Manager):
#property
def serializer_class(self):
from ow_articlecode.import_cycle_serializers import MyModelSerializer
return MyModelSerializer
However, this feels like a hack. What would be a proper solution to break this import cycle?
A Manager [Django-doc], has no serializer_class field. In fact a manager does not know anything about serialization. A manager is used to filter, create, etc. objects.
Your models.py thus should look like:
# app/models.py
from django.db.models import Manager, Model, CharField
class MyModelManager(Manager):
# no serializer_class
pass
class MyModel(Model):
name = CharField(max_length=64)
The idea of Models is that this defines your business logic, not the serialization, form, view, template logic.

What is the difference between use fields attribute and use a FormClass in Django CreateView?

What is the difference between use:
class FooCreateView(CreateView):
model = Foo
fields = (f,o,o)
and
class FooCreateView(CreateView):
model = Foo
form_class = FooForm
The difference is that in the second you need to define a FooForm class that must inherit from Django ModelForm class and have a model = Foo, this mechanism allows you to implement extra validations over your form (i.e. by defining methods like: def clean_f(self), def clean_o(self))
As you can see, you don't need to specify the fields attribute in the CreateView subclasss if using a form_class, because Django will take the form_class fields and use it.
Example:
models.py
from django.db import models
class Foo(models.Model):
f = models.CharField(max_length=10)
forms.py
from django import forms
from appname.models import Foo
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = '__all__' # shortcut to add all Foo fields.
def clean_f(self):
if 'M' not in self.cleaned_data['f']:
raise forms.ValidationError('M char must be in f attribute')
return self.cleaned_data['f']
views.py
from django.views.edit import CreateView
from appname.forms import FooForm
class FooCreateView(CreateView):
model = Foo
form_class = FooForm
This will raise a ValidationError if you try to save a Foo instance which does not contains a 'M' in their f attribute.

Removing fields from django-rest-framework view

is there any way to show only a list of fields or excluding some of them when using django-rest-framework?
Here's my app/views.py:
from rest_framework.generics import ListAPIView
from .models import PhpbbUsers
class UsersReadView(ListAPIView):
model = PhpbbUsers
Obiously there are some user information that I don't want to show to everyone. How could I do?
Solution code
from rest_framework import generics, serializers
from .models import PhpbbUsers
class UsersSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = PhpbbUsers
fields = ('user_id', 'username', 'user_avatar')
class UsersReadView(generics.ListAPIView):
model = PhpbbUsers
serializer_class = UsersSerializer
Set the serializer_class attribute on the view.
See the quickstart for a good example: http://django-rest-framework.org/tutorial/quickstart.html

Django - Dynamically importing a models form

I want to create a view that is able to show a ModelForm for various different models. It does this by obtaining the content type of the model and then dynamically instantiating the model form associated with that particular model. Here is my model:
from django.db import models
class SomeModel(models.Model):
name = models.CharField(max_length=150)
def __unicode__(self):
return self.name
And inside the same app there is a forms.py with the following form:
from django.forms import ModelForm
from someapp.models import SomeModel
class SomeModelForm(ModelForm):
class Meta:
model = SomeModel
fields = ('name',)
So what I want to do inside of my view file is return the correct form for each model dynamically. I tried the following:
from django.db import models
from someapp.forms import SomeModelForm
class SomeModel(models.Model):
name = models.CharField(max_length=150)
form = SomeModelForm
def __unicode__(self):
return self.name
But it doesn't work because of the obvious circular import. Does anyone have any idea how I might go about achieving this? I tried toying with modelform_factory, but it seems to ignore any of my custom model forms in forms.py.
EDIT: I should of mentioned that I won't have an instance of the model, just the model class itself, so having a method that inside of the model doesn't work (it does, however, work if you are calling it on an instance of the model)
You could get around the circular import by importing your model form inside a method.
class SomeModel(models.Model):
name = models.CharField(max_length=150)
#staticmethod
def get_form_class():
from someapp.forms import SomeModelForm
return SomeModelForm
# in your view:
SomeModel.get_form_class()
Putting the import within a method on the model should be enough to get you around the circular import, so instead of what you have, you'd use:
class SomeModel(models.Model):
...
def get_form(self):
from someapp.forms import SomeModelForm
return SomeModelForm
You can even make it a property if you want with:
form = property(get_form)
There is a built-in func get_model for lazy importing models.
from django.db.models import get_model
SomeModel = get_model('your_app_name', 'SomeModel')
Using __import__ and getattr.
# models.py
class SomeModel(models.Model):
...
#classmethod
def get_form(cls):
try:
app = __import__(cls._meta.app_label)
forms = getattr(app, "forms")
return getattr(forms, "%sForm" % cls.__name__)
except:
return None
# forms.py
class SomeModelForm(forms.Form):
...
in a view you can get the form associate to a models like this:
# views.p
from models import SomeModel
...
def myview(request):
form = SomeModel.getform()