Serialize foreign key object that is AbstractUser - django

I am serializing a model using the Django REST framework successfully, but would like to add a field from a related model. I have seen other posts describe how to do this using nested serializers, however mine is different because the other model I am trying to access is an AbstractUser class.
I would like to serialize the UserDefinedEquipName field from CustomUser.
models (some fields removed for clarity):
accounts/models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
UserDefinedEquipName = models.CharField(max_length=50, default = "Default equip",)
....
builds/models.py
from accounts.models import CustomUser
from django.contrib.auth import get_user_model
class Build(models.Model):
author = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,)
machineName = models.OneToOneField(max_length=50,blank=True,)
....
So my thought is to pass the value into the serializer but can't seem to figure out how to access the value without getting error AttributeError: type object 'Build' has no attribute 'CustomUser'
I have tried:
My serializers.py:
from rest_framework import serializers
from .models import Data, Build, CustomUser
from django.contrib.auth.models import AbstractUser
class buildStatsAPI_serializer(serializers.ModelSerializer):
equipName = Build.CustomUser.UserDefinedEquipName
#also tried:
#equipName = Build.CustomUser__set.all()
class Meta:
fields = ('id','author','machineName','equipName',)
model = Build
Am I missing something small here? Or is there a much better way of doing this. It seems like if this wasn't an AbstractUser class it would be much easier.
EDIT - Added views.py
class buildStatsAPI(generics.ListCreateAPIView):#for build stats JSON
permission_classes = (permissions.IsAuthenticated,)
serializer_class = buildStatsAPI_serializer
def get_queryset(self):
machinesOwned =CustomUser.objects.filter(customerTag=self.request.user.customerTag).filter(isDevice=True)
machineList = []
for machine in machinesOwned:
machineList = machineList + [machine.id]
query = Build.objects.filter(deleted=0, author_id__in=machineList,).values().order_by('pk')
return query

I think you are defining the Serializer improperly. You can't directly reference Model in a serializer. You need to use any kind of fields. For example, if you use SerializerMethodField, you can try like this:
class buildStatsAPI_serializer(serializers.ModelSerializer):
equipName = serializers.SerializerMethodField()
class Meta:
fields = ('id','author','machineName','equipName',)
model = Build
def get_equipName(self, obj):
# here obj is a build model object
return obj.author.UserDefinedEquipName
Update
Please update your get_queryset method so that it returns a queryset like this(I have refactored it a bit):
def get_queryset(self):
query = Build.objects.filter(deleted=0, author__customerTag=self.request.user.customerTag, author__isDevice=True) # removed .values() because it will return a dictionary
return query

Related

How to rename GraphQL type field autogenerated from Django model?

I've a model in Django with some fields. Let's say this exemplary one:
# <app>/models.py
from django.db import models
class Something(models.Model):
first_field = models.Charfield()
second_field = models.Charfield()
I use DjangoObjectType from graphene_django to map Django models to GraphQL types.
# <app>/schema.py
from graphene_django import DjangoObjectType
from .models import Something
class SomethingType(DjangoObjectType):
class Meta:
model = Something
Cause of auto camelcasing the model field second_field results in secondField in the GraphQL type. Now I'd like to rename the GraphQL type field from secondField to somethingFancy. How can I get this done easiest?
You can overwrite any field with DjangoObjectType. Your code may look like this.
class SomethingType(DjangoObjectType):
class Meta:
model = Something
something_fency = graphene.String()
def resolve_something_fency(self, info):
return self.second_field
For more details check out the docs

How to specify which value is returned for a foreign field when serialising data in a view in Django

I have a model containing a ForeignKey to another model. I am attempting to serialize this model and want to control what field is returned for the foreignkey field. See below:
models.py
class Surveyor(models.Model):
num = models.CharField(max_length=3)
name = models.CharField(max_length=250)
class Anblsrecord(models.Model):
...
sur_num = models.ForeignKey(Surveyor, on_delete=models.CASCADE)
views.py
def anbls_points(request):
points_as_geojson = serialize('geojson', Anblsrecord.objects.all()[:5], fields=(... 'sur_num'))
return JsonResponse(json.loads(points_as_geojson))
When I view this I get:
... "sur_num": 1 ...
where the "1" is "num" from Surveyor class. I want to return "name".
I looked at https://docs.djangoproject.com/en/2.2/topics/serialization/ which talks about multi-table inheritance, but I can't find anything for a related table.
Any help would be greatly appreciated.
Django Rest Framework serializers with django-rest-framework-gis worked:
serializers.py
from anblsrecords import models
from rest_framework_gis.serializers import GeoFeatureModelSerializer
class AnblsrecordSerializer(GeoFeatureModelSerializer):
sur_name = serializers.CharField(source='sur_num.name')
class Meta:
model = models.Anblsrecord
geo_field = "geom"
fields = (
...
'sur_name',
)
views.py
from rest_framework import generics
class ListAnbls_points(generics.ListCreateAPIView):
queryset = Anblsrecord.objects.all()[:5]
serializer_class = serializers.AnblsrecordSerializer
This returns:
"properties": {
...,
"sur_name": "Name of Surveyor",...}, and includes the geometry feature.

After update to DRF 3.0 and Django 1.8, Serialiser method invalid

I am not sure whether it is related or not. But before I update to DRF 3.0 and Django 1.8, my following code is working properly.
class DialogueSerializer(serializers.ModelSerializer):
sound_url = serializers.SerializerMethodField('get_sound_url')
class Meta:
model = Dialogue
fields = ('id','english','myanmar', 'sound_url')
def get_sound_url(self, dialogue):
if not dialogue.sound:
return None
request = self.context.get('request')
sound_url = dialogue.sound.url
return request.build_absolute_uri(sound_url)
Some people also said like this.
https://github.com/lightweightdjango/examples/issues/2
Now, when I run, I got
It is redundant to specify get_sound_url on SerializerMethodField
'sound_url' in serializer 'DialogueSerializer', because it is the same
as the default method name. Remove the method_name argument.
How shall I do?
Since your field is sound_url and your method get_field_name (get_sound_url), you don't have to give to SerializerMethodField the method name.
As you can see in the example from DRF documentation, there is no need to precise method_name in this case.
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
So, the result is :
class DialogueSerializer(serializers.ModelSerializer):
sound_url = serializers.SerializerMethodField() # no more `method_name` passed
class Meta:
model = Dialogue
fields = ('id','english','myanmar', 'sound_url')
def get_sound_url(self, dialogue):
if not dialogue.sound:
return None
request = self.context.get('request')
sound_url = dialogue.sound.url
return request.build_absolute_uri(sound_url)

Retrieving the Model object using table name

I would like to retrieve the Model object while knowing the table_name
For ex:
class User(models.Model):
class Meta:
db_table = 'my_users_table'
Are there ways that return User by taking my_users_table as input?
I would like to retrieve the Model object
I think you mean the Model class here instead of object.
One possible solution that I can think of is to get all the models and match for db_table:
from django.apps import apps
model = next((m for m in apps.get_models() if m._meta.db_table=='my_users_table'), None)
If there is no model with the given db_table name then the model will be None.
I don't think there is a direct way to do this. But you can create your own function which will do this for you.
Based on https://stackoverflow.com/a/29739109/3627387
from django.apps import apps
def get_model_by_db_table(db_table):
for model in apps.get_models():
if model._meta.db_table == db_table:
return model
else:
# here you can do fallback logic if no model with db_table found
raise ValueError('No model found with db_table {}!'.format(db_table))
# or return None
Or you can do this using ContentType model
Edit:
The link provided for ContentType is broken. This may be tried instead.

How to allow generic filtering with djangorestframework on all fields?

My packages:
djangorestframework==3.2.5
django-filter==0.11.0
djangorestframework-filters==0.5.0
I'm using djangorestframework to expose my models as a restful api. To make my and the frontend dev's life easier I want to allow any filter lookup on any model field. But as far as I could see neither of the packages supports such generic filtering out of the box. I can use a AllLookupsFilter but still need to create a filterset class per model and specify each field.
Is there a generic approach to allow filtering on all models and all fields?
I created a little helper function that creates the filterset on-the-fly based on the queryset model of the modelviewset. It adds an AllLookupFilter for each field in the model.
from rest_framework import viewsets
import rest_framework_filters as filters
def create_universal_filter(klass):
"""Creates filterset class with all lookups for all fields of given class"""
field_filters = dict((f, filters.ALL_LOOKUPS)
for f in klass._meta.get_all_field_names())
class MyFilter(filters.FilterSet):
class Meta:
model = klass
fields = field_filters
return MyFilter
class GenericFilterModelViewSet(viewsets.ModelViewSet):
"""Allows all lookups on all fields"""
def __init__(self, *args, **kwargs):
self.filter_class = create_universal_filter(self.queryset.model)
Then I let the modelviewsets where I want to allow generic filtering inherit from it:
class DerivateGroupViewSet(GenericFilterModelViewSet):
queryset = models.DerivateGroup.objects.all()
serializer_class = serializers.DerivateGroupSerializer
You can just add filter_backends to all needed views.
from rest_framework.filters import SearchFilter
class SimpleClass(generics.ListCreateAPIView):
serializer_class = SimpleSerializer
filter_backends = [SearchFilter]