I'm currently doing the tutorial on Relationships and hyperlinked API's. However I've come across a strange problem that I can't seem to fix. My serializers.HyperlinkedIdentityField and serializers.HyperlinkedRelatedField doesnt seem to detect the namespace I'm using.
My serializers look like this
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='snippets:snippet-highlight', format='html')
class Meta:
model = Snippet
fields = ('url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style')
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippets:snippet-detail', read_only=True)
class Meta:
model = User
fields = ('url', 'id', 'username', 'snippets')
Which is pretty much the same as the tutorial except I am adding view_name='snippets:snippet-detail' in the serializer field.
I am creating my namespace as suggested by the Django documentation, by adding app_name = 'snippets' above my urlpatterns.
This is the error I'm getting
ImproperlyConfigured at /snippets/
Could not resolve URL for hyperlinked relationship using view name
"snippet-detail". You may have failed to include the related model in
your API, or incorrectly configured the lookup_field attribute on
this field.
As you see, I have approached the problem the same way other people have but without resolving the issue. Anyone have an idea about what I could try next?
Solved the problem after reading some more about Hyperlinks and noticing that I should add extra_kwargs for the url field SnippetSerializer
class Meta:
model = Snippet
fields = ('url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style')
extra_kwargs = {
'url': {'view_name': 'snippets:snippet-detail'},
}
And UserSerializer
class Meta:
model = User
fields = ('url', 'id', 'username', 'snippets')
extra_kwargs = {
'url': {'view_name': 'snippets:user-detail'},
}
fix this issue by modify serializer.py
we can define url, such as:
url = serializers.HyperlinkedIdentityField(view_name='snippets:user-detail', lookup_field='pk')
or define in Meta, such as
extra_kwargs = {
'url': {'view_name': 'snippet:user-detail', 'lookup_field': 'pk'},
}
all serializer.py code:
from rest_framework import serializers
from snippets.models import Snippet
from django.contrib.auth.models import User
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(lookup_field="pk", view_name='snippets:snippet-highlight', format='html')
class Meta:
model = Snippet
fields = ('url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style')
extra_kwargs = {
'url': {'view_name': 'snippets:snippet-detail', 'lookup_field': 'pk'},
}
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(lookup_field="pk", many=True, view_name='snippets:snippet-detail', read_only=True)
url = serializers.HyperlinkedIdentityField(view_name='snippets:user-detail', lookup_field='pk')
class Meta:
model = User
fields = ('url', 'id', 'username', 'snippets')
# extra_kwargs = {
# 'url': {'view_name': 'snippet:user-detail', 'lookup_field': 'pk'},
# }
Try to pass lookup_field and lookup_url_kwarg arguments.
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippets:snippet-detail',
lookup_field="",
lookup_url_kwarg="", read_only=True)
Refer HyperlinkedRelatedFieldDoc
Related
When creating an object in my api view it seems fine but whenever I update the object even I didn't change my email field it says user with this Email Address already exists. I am using generics.RetrieveUpdateView for my view.
I thought generics.RetrieveUpdateView automatically handles this. What am I missing?
my model looks like this:
class User(AbstractUser):
email= models.EmailField(unique=True)
...
my serializer looks like this:
class UserListForProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username','email','last_login', 'date_joined','is_active', 'is_student', 'is_staff', 'is_teacher', 'is_registrar', 'is_superuser']
read_only_fields = ('username','date_joined', 'last_login','is_active', 'is_student', 'is_staff', 'is_teacher', 'is_registrar', 'is_superuser')
class StaffProfileDetailSerializer(CreateProfileSerializer):
user = UserListForProfileSerializer(many= False)
class Meta:
model = StaffProfile
fields = ['user','first_name', 'middle_name', 'last_name', 'gender', 'employee_number', 'date_of_birth', 'mobile_number','dob_month','dob_day','dob_year','address',]
read_only_fields = ('date_joined', 'last_login',)
my view looks like this:
class UserProfileDetail(generics.RetrieveUpdateAPIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = StaffProfileDetailSerializer
def get_queryset(self, *args, **kwargs):
userid = self.request.user.id
return StaffProfile.objects.filter(pk= userid)
urls.py:
urlpatterns = [
path('api/', apiOverview, name='apioverview'),
path('api/user/profile/detail/<int:pk>/', UserProfileDetail.as_view(),
name='userprofile-detail'),
...
]
I have some Django models with different relations to each other — Many-to-many, and Foreignkey. By that means, I want to serialize them using djnago-rest.
Here are the models:
class CommonFieldsAbstract(models.Model):
name = models.CharField(max_length=30, unique=True)
class ServerModel(CommonFieldsAbstract):
server_ip = models.GenericIPAddressField(default='172.17.0.1')
server_port = models.IntegerField(default='9001')
class SNMPLineModel(CommonFieldsAbstract):
ip_address = models.GenericIPAddressField()
port = models.IntegerField(default=161)
class SNMPModel(CommonFieldsAbstract): # target
line = models.ForeignKey(SNMPLineModel, on_delete=CASCADE)
servers = models.ManyToManyField(ServerModel)
class MetaDataModel(models.Model):
key = models.CharField(max_length=20)
value = models.CharField(max_length=20)
snmp_device = models.ForeignKey(SNMPModel, on_delete=CASCADE)
Before, I used to use the following approach to create the JSON manually:
def meta_data_json(meta_data):
meta_data_list = []
for meta in meta_data:
meta_data_list.append({
meta.key: meta.value
})
return meta_data_list
def server_json(servers):
return [{'ip': server.server_ip,
'port': server.server_port}
for server in servers]
def create_json():
snmp = SNMPModel.objects.filter(name__contains='a-name')
return {
'name': snmp.name,
'address': snmp.line.ip_address,
'port': snmp.line.port,
'servers': server_json(snmp.servers.all()),
'meta_data': meta_data_json(MetaDataModel.objects.filter(
snmp_device=snmp.pk
)
),
'device_pk': snmp.pk
}
My Question:
Now, how can I create such an above json via django-rest-framework instead?
I don't have any problem with many-to-many fields. In fact, my problem is the foreignkey(s).
Here's what I've done so far:
# serializers.py
from rest_framework import serializers
class MetaDataSerializer(serializers.ModelSerializer):
class Meta:
fields = [
'id',
'key',
'value',
]
model = MetaDataModel
class ServerSerializer(serializers.ModelSerializer):
class Meta:
fields = [
'id',
'server_ip',
'server_port',
]
model = ServerModel
class LineSerializer(serializers.ModelSerializer):
port = serializers.RelatedField(many=True)
class Meta:
fields = '__all__'
model = SNMPLineModel
class SNMPSerializer(serializers.ModelSerializer):
servers = ServerSerializer(many=True, read_only=True) # It is ok
meta_data = MetaDataSerializer(many=True, read_only=True) # It's not ok
line = LineSerializer(many=True, read_only=True) # It's not ok
address = serializers.CharField(source=SNMPLineModel.ip_address) # It's not ok
port = serializers.CharField(source=SNMPLineModel.port) # It's not ok
class Meta:
fields = [
'id',
'servers',
'name',
'address',
'port',
'line',
'meta_data'
]
model = SNMPModel
# views.py
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, JsonResponse
#csrf_exempt
def snippet_detail(request, name):
try:
snmp_conf = SNMPModel.objects.filter(name__contains=name)
except SNMPModel.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
serializer = SNMPSerializer(snmp_conf, many=True)
return JsonResponse(serializer.data, status=200, safe=False)
# urls.py
from django.urls import path
urlpatterns = [
path('snippets/<name>/', views.snippet_detail)
]
Any help would be greatly appreciated.
The serializers.SerializerMethodField() is a useful method to add in relations like this.
get_meta_data() is a bit of magic evaluating the fieldname to call the method.
Address and port seem to be a simple relation and line.FOO should work.
class SNMPSerializer(serializers.ModelSerializer):
servers = ServerSerializer(many=True, read_only=True) # It is ok
meta_data = serializers.SerializerMethodField()
line = serializers.SerializerMethodField()
address = serializers.CharField(source="line.ip_address", read_only=True)
port = serializers.CharField(source="line.port" , read_only=True)
class Meta:
fields = ['id', 'servers', 'name', 'address', 'port', 'line', 'meta_data']
model = SNMPModel
def get_meta_data(self, instance):
metadatamodels = MetaDataModel.objects.filter(snmp_device=instance)
serializer = MetaDataSerializer(instance=metadatamodels, many=True, read_only=True)
return serializer.data
def get_line(self, instance):
serializer = LineSerializer(instance.line, read_only=True)
return serializer.data
For solving the mentioned problems I did the following solutions:
For a Forward Foreignkey you do not need many=True
For a Reverse Foreignkey you need using related-model_set while you haven't defined related_name.
For an extra field you just need the exact queryset.
Therefore, I reached the following code snippet without using .SerializerMethodField() as Michael used on his answer:
class SNMPSerializer(serializers.ModelSerializer):
servers = ServerSerializer(many=True)
meta_data = MetaDataSerializer(many=True, source="metadatamodel_set")
line = LineSerializer()
address = serializers.CharField(source="line.ip_address")
port = serializers.CharField(source="line.port")
class Meta:
fields = (
"id",
"servers",
"name",
"address",
"port",
"line",
"meta_data",
)
model = SNMPModel
I created a serializer which the "user" below is from another Serializer which i imported, now the imported serializer(PubliceProfileSerializer) works fine on its own but it does not display the content of USER when i call it my browser from this serializer. Every other item shows except the user. Please help
from rest_framework import serializers
from users.api.serializers import PublicProfileSerializer
from blog.models import Post
class PostSerializer(serializers.ModelSerializer):
user = PublicProfileSerializer(source='users.profile', read_only=True)
category = serializers.SerializerMethodField()
label = serializers.SerializerMethodField()
class Meta:
model = Post
fields = '__all__'
def get_category(self, obj):
return obj.get_category_display()
def get_label(self, obj):
return obj.get_label_display()
Add you your serializer the list of fields. Replace
fields = '__all__'
with
fields = ('id', 'user', 'category', 'label')
because:
fields = '__all__'
will only populate id, category and label from the Post model, but will not provide the nested user serializer, so it becomes:
class Meta:
model = Post
fields = ('id', 'user', 'category', 'label')
or
class PostSerializer(serializers.ModelSerializer):
user = PublicProfileSerializer(many=False,
source='users.profile',
read_only=True)
category = serializers.SerializerMethodField()
label = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('id', 'user', 'category', 'label')
def get_category(self, obj):
return obj.get_category_display()
def get_label(self, obj):
return obj.get_label_display()
I have HCL AppScan CodeSweep installed on VScode and it picked up an SSL context vulnerability on one of my Django serializers and I was just wondering I can go about fixing it without changing my desired functionality.
class CategoryDetailSerializer(CategoryListSerializer):
products = serializers.SerializerMethodField()
class Meta:
model = Category
fields = (
'id',
'title',
'products',
)
def get_products(self, obj):
# The source of the SSL context override
return ProductListSerializer(obj.product_set.all(), many=True, context=self.context).data
class ProductListSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='products:product-detail-view', lookup_field='slug')
class Meta:
model = Product
fields = (
'id',
'slug',
'title',
'price',
'image',
'url',
)
So I am writing a UserDetails view as follows.
class UserDetailsView(RetrieveUpdateAPIView):
serializer_class = AuthUserSerializer
def get_object(self):
return self.request.user
My Serializers are as follows.
class PermissionSerializer(serializers.ModelSerializer):
class Meta:
model = Permission
fields = ('id', 'name', 'codename')
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id', 'name')
class AuthUserSerializer(serializers.ModelSerializer):
groups = GroupSerializer(many=True)
# permissions = PermissionSerializer(many=True)
# permissions = serializers.PrimaryKeyRelatedField(many=True, queryset=Permission.objects.all())
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'email',
'is_staff', 'is_active', 'is_superuser', 'last_login',
'date_joined', 'groups', 'user_permissions')
groups = GroupSerializer(many=True) gives me following.
"groups": [
{
"id": 2,
"name": "A"
},
{
"id": 1,
"name": "B"
},
{
"id": 3,
"name": "C"
}
],
I expect the similar from permissions = PermissionSerializer(many=True) but I get the following error.
Got AttributeError when attempting to get a value for field `permissions` on serializer `AuthUserSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'permissions'.
but instead, if add user_permissions to the fields directly without adding related reference it gives me all ids of permissions. I want to have id, name, codename also. And, Of course, UserPermissions model is not found. ;-(
How do I fix this?
You can use source parameter on the Serializer.
class AuthUserSerializer(serializers.ModelSerializer):
groups = GroupSerializer(many=True)
permissions = PermissionSerializer(many=True, source='user_permissions')
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'email',
'is_staff', 'is_active', 'is_superuser', 'last_login',
'date_joined', 'groups', 'user_permissions', 'permissions')