I'm having problems with Django Rest Framework reverse function (from rest_framework.reverse import reverse) and django_reverse.
When I try to return the url of a single object, I'm getting all the url pattern, instead of the ID I get the text representation of the model.
For example I have a model called Driver, when I try to get the url of a Driver instance with pk=1, the response is
http : //base_url/driver/driver%20object/.
This can be resolve by overriding Driver unicode function to return str(self.pk), but I don't like this approach.
Also previous versions of django/drf did not present this issue.
First of all my virtual-env is:
Django==1.8.4
django-braces==1.8.0
django-extensions==1.5.5
django-filter==0.10.0
django-oauth-toolkit==0.8.1
djangorestframework==3.3.2
psycopg2==2.6
etc
Models
class Dominio(AuditableModel):
dominio = models.CharField(max_length=200, blank=False, null=False, unique=True)
pais = models.ForeignKey(Pais, null=False, blank=False)
#property
def owners(self):
if self.conductores.count() > 0:
return self.conductores.filter(is_owner=True).all()[0]
else:
return None
class Conductor(AuditableModel):
usuario = models.ForeignKey(User, null=False, blank=False, related_name='dominios')
dominio = models.ForeignKey(Dominio, null=False, blank=False, related_name='conductores')
is_owner = models.BooleanField(default=False, null=False)
#
# def __unicode__(self):
# return str(self.pk)
class Meta:
verbose_name_plural = "conductores"
Serializers
class ConductorSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Conductor
fields = ('id', 'url', 'usuario', 'dominio', 'is_owner')
class DominioSerializer(serializers.HyperlinkedModelSerializer):
owners = serializers.HyperlinkedRelatedField(read_only=True, view_name='conductor-detail')
class Meta:
model = Dominio
fields = ('id', 'url', 'dominio', 'pais', 'owners')
result
"owners": "http : //localhost:8000/ventana/api/v1/conductores/Conductor%20object/"
Django rest framework api response
but, if I change the #property owners method to return a list of drivers (conductores), then the urls are correct
Models
class Dominio(AuditableModel):
dominio = models.CharField(max_length=200, blank=False, null=False, unique=True)
pais = models.ForeignKey(Pais, null=False, blank=False)
#property
def owners(self):
if self.conductores.count() > 0:
return self.conductores.filter(is_owner=True).all()
else:
return []
Serializers
class DominioSerializer(serializers.HyperlinkedModelSerializer):
owners = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='conductor-detail')
class Meta:
model = Dominio
fields = ('id', 'url', 'dominio', 'pais', 'owners')
Result
"owners": [ "http : //localhost:8000/ventana/api/v1/conductores/1/"]
Django rest framework api response 2
Related
I created two models:
parcel (package) model,
'shelf' model to which parcels can be assigned.
class Parcel(models.Model):
owner = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
name = models.CharField(max_length=100, blank=False)
contents = models.TextField(blank=False, validators=[MaxLengthValidator(1500)])
size = models.CharField(max_length=50, blank=True, validators=[package_size_validator])
weight = models.PositiveIntegerField(blank=True, null=True)
contact = models.CharField(max_length=50, blank=True)
creation_date = models.DateTimeField(auto_now_add=True)
last_modification = models.DateTimeField(auto_now=True)
code = models.CharField(max_length=30, unique=True, blank=False)
def __str__(self):
return f'Parcel: {self.code}, {self.owner}, {self.name}'
class ParcelShelf(models.Model):
owner = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
name = models.CharField(max_length=100, blank=False)
creation_date = models.DateTimeField(auto_now_add=True)
last_modification = models.DateTimeField(auto_now=True)
parcels = models.ManyToManyField('Parcel', blank=True, related_name='shelf_parcel')
def __str__(self):
return f'ParcelShelf: {self.owner}, {self.name}'
I came to a solution where the logged-in user can see only his packages and shelves. The problem I have is with the many-to-many relationship where parcels can be added to shelves. I want to come to a solution where the logged in user can add to the shelf only those parcels which he is the owner, creator. It will look better in pictures.
All packages created by user t2#t2.com (user id = 17):
parcels list
Now the view when the user t2#t2.com wants to create a shelf:
shelf list
All packages are visible, while only those created by the user t2#t2.com should be available.
Code to serializer:
class ParcelShelfSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
parcels = serializers.HyperlinkedRelatedField(many=True, read_only=False, view_name='parcels_detail_view',
# queryset=Parcel.objects.filter(owner=17)
queryset=Parcel.objects.all()
)
class Meta:
model = ParcelShelf
fields = ('id', 'owner', 'name', 'creation_date', 'last_modification', 'parcels')
Below is a picture where only packages for a given, logged-in user are available:
shelf list
Code to serializer:
class ParcelShelfSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
parcels = serializers.HyperlinkedRelatedField(many=True, read_only=False, view_name='parcels_detail_view',
queryset=Parcel.objects.filter(owner=17)
# queryset=Parcel.objects.all()
)
class Meta:
model = ParcelShelf
fields = ('id', 'owner', 'name', 'creation_date', 'last_modification', 'parcels')
I got to the point where the 'solution' is in the 'queryset' argument.
All users: queryset=Parcel.objects.all()
Logged in user: queryset=Parcel.objects.filter(owner=17)
The problem is, this is hardcoded, and it should be something like: (owner=request.user). Unfortunately, I don't know how to achieve this in the serializer. I looked through other similar topics, but I didn't find a solution how to use the request method in the serializer field.
In addition, code in views:
class ParcelsShelfList(generics.ListCreateAPIView):
# queryset = ParcelShelf.objects.all()
serializer_class = ParcelShelfSerializer
def get_queryset(self):
user = self.request.user
if bool(user and user.is_staff and user.is_admin):
return ParcelShelf.objects.all()
else:
return ParcelShelf.objects.filter(owner=user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
UPDATE
Thanks to the help of #Amrez, who gave me a link to a similar topic, i was able to do it.
mentioned link: How can I filter DRF serializer HyperlinkedRelationField queryset based on request data?
I add this to my code in serializers.py:
def hyperlinked_related_field_by_owner(model, view_name, owner):
return serializers.HyperlinkedRelatedField(
many=True,
view_name=view_name,
queryset=model.objects.filter(owner=owner)
)
class ParcelShelfSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
parcels = serializers.HyperlinkedRelatedField(many=True,
read_only=False,
view_name='parcels_detail_view',
# queryset=Parcel.objects.filter(owner=17)
queryset=Parcel.objects.all()
)
def get_fields(self):
fields = super().get_fields()
owner = self.context['request'].user
fields['parcels'] = hyperlinked_related_field_by_owner(Parcel, 'parcels_detail_view', owner)
return fields
class Meta:
model = ParcelShelf
fields = ('id', 'owner', 'name', 'creation_date', 'last_modification', 'parcels')
I've set up my models, serializers and viewsets in my Django REST API to assign a search record to a particular user, and to associate all the relevant user's searches to their record in the User model. It was all working fine, but I'm now getting the TypeError error message (in the subject line of this question) when I try to create a new user. I've listed the relevant models, serializers and viewsets below. Please could anyone take a look and let me know where I'm going wrong? Any help would be very much appreciated.
User serializer:
class UserSerializer(serializers.ModelSerializer):
searches = serializers.PrimaryKeyRelatedField(many=True, queryset=SearchHistoryModel.objects.all())
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'searches')
extra_kwargs = {'email': {
'required': True,
'validators': [UniqueValidator(queryset=User.objects.all())]
}}
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
Token.objects.create(user=user)
return user
User viewset:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.AllowAny]
Search model:
class SearchHistoryModel(models.Model):
"""
Stores each user's search submission
"""
created_date = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, related_name='searches', on_delete=models.CASCADE)
cpu_component_name = models.CharField(max_length=10, blank=False)
cpu_subcomponent_name = models.CharField(max_length=50, blank=False)
motherboard_name = models.CharField(max_length=20, blank=False)
gpu_component_name = models.CharField(max_length=10, blank=True, null=True)
gpu_subcomponent_name = models.CharField(max_length=50, blank=True, null=True)
gpu_subcomponent_quantity = models.PositiveIntegerField(default=0)
ram_component_name = models.CharField(max_length=15, blank=True, null=True)
ram_component_quantity = models.PositiveIntegerField(default=0)
ssd_component_name = models.CharField(max_length=15, blank=True, null=True)
ssd_component_quantity = models.PositiveIntegerField(default=0)
hdd_component_name = models.CharField(max_length=20, blank=True, null=True)
hdd_component_quantity = models.PositiveIntegerField(default=0)
optical_drive_name = models.CharField(max_length=15, blank=True, null=True)
class Meta:
verbose_name = 'Search'
verbose_name_plural = 'Searches'
ordering = ['owner', 'created_date']
def __str__(self):
return '{}\'s search choices'.format(self.owner)
Search serializer:
class SearchHistorySerializer(serializers.ModelSerializer):
"""
Serializes the user's search history data passed into the SearchHistoryModel
Associates each search with the relevant user
"""
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = SearchHistoryModel
fields = (
'id', 'created_date', 'owner', 'cpu_component_name', 'cpu_subcomponent_name',
'motherboard_name', 'gpu_component_name', 'gpu_subcomponent_name',
'gpu_subcomponent_quantity', 'ram_component_name', 'ram_component_quantity',
'ssd_component_name', 'ssd_component_quantity', 'hdd_component_name',
'hdd_component_quantity', 'optical_drive_name'
)
Search viewset:
class SearchHistoryViewSet(viewsets.ModelViewSet):
queryset = SearchHistoryModel.objects.all()
serializer_class = SearchHistorySerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
In user = User.objects.create_user(**validated_data), validated_data contains a searches value which is an id.
But actually the ForeignKey is in the other sense : in Searches model, and to refer to a User instance, not the opposite.
To link a user to searches, it is not in User DB table that you write an id, but in Searches that you write a User id.
class UserSerializer(serializers.ModelSerializer):
(...)
def create(self, validated_data):
# Extract the value from 'validated_data'
search_ids = validated_data.pop('searches', None)
user = User.objects.create_user(**validated_data)
Token.objects.create(user=user)
# Update existing search instances
for search_id in search_ids:
Search.objects.filter(id=search_id).update(owner=user)
return user
I found a lot of answers to the similar issue, but none of them helped me.
I am new to the backend and Django, I already spent a few days trying figure out what I am doing wrong, but no success.
I would appreciate any help a lot!
So, when I call http://127.0.0.1:8000/users/{user_name}/
I am getting :
ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "post-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
If I change HyperlinkedRelatedField on any other field it's working properly...
urls.py
app_name = 'api'
urlpatterns = [
url(r'^posts/(?P<post_id>\d+)/$', PostDetails.as_view(),
name='post-detail'),
url(r'^users/(?P<username>[\w\-]+)/$', UserPosts.as_view()),
]
views.py
class PostDetails(APIView):
"""
- GET a post
"""
def get(self, request, post_id):
post = Post.objects.get(id=post_id)
post_serializer = PostSerializer(post)
return Response(post_serializer.data)
class UserPosts(APIView):
"""
GET all user posts
"""
def get(self, request, username):
user = User.objects.get(username=username)
serializer = UserSerializer(user, context={'request': request})
return Response(serializer.data)
serializer.py
class UserSerializer(serializers.ModelSerializer):
posts = serializers.HyperlinkedRelatedField(many=True,
read_only=True,
view_name='post-detail',
lookup_field='id')
# Mandatory for UUID serialization
user_id = serializers.UUIDField()
class Meta:
model = User
exclude = ('id', 'password')
read_only_fields = ('created', 'username', 'posts',)
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer()
class Meta:
model = Post
fields = '__all__'
models.py
class User(models.Model):
username = models.CharField(max_length=30, unique=True)
password = models.CharField(max_length=50)
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
phone = models.CharField(max_length=20, blank=False, unique=True)
user_id = models.UUIDField(editable=False,
unique=True,
null=False,
db_index=True,)
created = models.DateTimeField()
id = models.BigAutoField(primary_key=True)
class Meta:
ordering = ('created',)
def __unicode__(self):
return "Email: %s " % self.email
class Post(models.Model):
created = models.DateTimeField()
is_active = models.BooleanField(default=False)
title = models.CharField(max_length=200, blank=False)
body_text = models.CharField(max_length=1000, blank=False)
address = models.CharField(max_length=100)
author = models.ForeignKey(User, on_delete=models.PROTECT,
related_name='posts')
price = models.DecimalField(max_digits=10, decimal_places=0)
id = models.BigAutoField(primary_key=True)
class Meta:
ordering = ('created',)
def __unicode__(self):
return "Title : %s , Author: %s " % (self.title, self.author)
Your lookup_field does not match your url one which is post_id
url(r'^posts/(?P<post_id>\d+)/$', PostDetails.as_view(),
name='post-detail'),
From docs:
lookup_field - The field on the target that should be used for the lookup. Should correspond to a URL keyword argument on the referenced view. Default is 'pk'.
lookup_url_kwarg - The name of the keyword argument defined in the URL conf that corresponds to the lookup field. Defaults to using the same value as lookup_field.
So you should be fine with this:
posts = serializers.HyperlinkedRelatedField(many=True,
read_only=True,
view_name='post-detail',
lookup_url_kwarg='post_id')
In my Django REST framework project i have this model:
class ml_request(models.Model):
model_id = models.CharField(max_length=100)
created = models.DateTimeField(auto_now=True)
p_country = models.CharField(max_length=100, blank=False, default='')
p_description = models.TextField(null=False, blank=False)
p_designation = models.CharField(max_length=200, blank=False, default='')
p_points = models.IntegerField(default=00)
p_price = models.IntegerField(default=00, blank=False)
p_province = models.CharField(max_length=100, blank=False, default='')
p_region_1 = models.CharField(max_length=100, blank=False, default='')
p_region_2 = models.CharField(max_length=100, blank=False, default='')
p_variety = models.CharField(max_length=100, blank=False, default='')
p_winery = models.CharField(max_length=100, blank=False, default='')
owner = models.ForeignKey('auth.User', related_name='req_owner',
on_delete=models.CASCADE)
highlighted = models.TextField()
class Meta:
ordering = ('created',)
then i create my serializer like this:
from rest_framework import serializers
from botoapi.models import ml_request, ml_response, LANGUAGE_CHOICES,
STYLE_CHOICES, ml_logs
from django.contrib.auth.models import User
class RequestSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(label='ID', read_only=True)
highlight = serializers.HyperlinkedIdentityField(view_name='request-highlight', format='html')
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = ml_request
fields = ('id', 'model_id', 'highlight', 'created', 'p_country',
'p_description', 'p_designation',
'p_points', 'p_price', 'p_province', 'p_region_1', 'p_region_2',
'p_variety', 'p_winery', 'owner')
def create(self, validated_data):
log_save = ml_logs(l_verbose=validated_data, l_method="CREATE",
l_action=validated_data.get("model_id", None))
log_save.save()
return validated_data
and is my view code:
class RequestViewSet(viewsets.ModelViewSet):
queryset = ml_request.objects.all()
serializer_class = RequestSerializer
permission_classes = (IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
#detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
bur when i run it and try to add values django return this error:
AttributeError at /requests/
'dict' object has no attribute 'pk'
Request Method: POST
Request URL: http://127.0.0.1:8000/requests/
Django Version: 1.11.7
Exception Type: AttributeError
Exception Value:
'dict' object has no attribute 'pk'
i have the id as PK and i add it in my serializer, someone can help me to wonder why this happens?
Thanks in advance
From 3.x onwards,
If you want to add django auto 'id' explicitly in HyperlinkedModelSerializer, you need to use ReadOnlyField (not an IntegerField). Or else you can include it in 'fields'.
class RequestSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
And by default the serializer will include a url field instead of a primary key field. You can explicitly include the primary key by adding it to the fields option.
The name of the URL field defaults to 'url'. You can override this
globally, by using the URL_FIELD_NAME setting.
I'm migrating my Python to a REST API, I'm using Django framework 1.8.
I followed the tutorial and one of my requests is to insert an Object (Server) which depends on few fk. In my Python App I normally have 3 queries to insert the records, use RETURNING id to store the Ids of the FKs and finally 1 query to insert my Server object. How can I achieve this in Django?
These are my models and serializer:
serializers.py
#Server Serializer
class ServerSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Server
fields = ('id', 'owner', 'discovered', 'status', 'serialnumber', 'hostname', 'description', 'created')
models.py
class Server(models.Model):
owner = models.ForeignKey('auth.User', related_name='server')
discovered = models.BooleanField(default=False)
status = models.SmallIntegerField(default=0)
serialnumber = models.CharField(max_length=100, null=True)
hostname = models.CharField(max_length=250)
description = models.CharField(max_length=250, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
fk_server_model = models.ForeignKey('ServerModel', db_column='fk_server_model')
fk_licensing = models.ForeignKey(Licensing, db_column='fk_licensing', blank=True, null=True)
fk_network_services = models.ForeignKey(NetworkServices, db_column='fk_network_services', blank=True, null=True)
fk_network_interfaces = models.ForeignKey(NetworkInterfaces, db_column='fk_network_interfaces', blank=True, null=True)
fk_server_credentials = models.ForeignKey('ServerCredentials', db_column='fk_server_credentials', blank=True, null=True)
def save(self, *args, **kwargs):
super(Server, self).save(*args, **kwargs)
class Meta:
managed = False
db_table = 'server'