I'm trying to use TastyPie Patch to a many-to-many, but I get this error:
"error_message": "Tastypie requires a Python-style path () to lazy load related resources. Only given 'SchemeResource'.",
Why?
The patch I'm making:
/participant/84
POST: {"email":"test#test.com", "schemes":{"id":"12", "schemes"}}
Resource:
class ParticipantResource(ModelResource):
schemes = fields.ToManyField('SchemeResource', attribute='schemes', full=True, null=True)
class Meta:
queryset = Participant.objects.all()
resource_name = 'participant'
allowed_methods = ['post', 'get', 'put', 'patch']
2nd Resource:
class SchemeResource(ModelResource):
user = fields.ToOneField(UserResource, 'user', full=True)
link = fields.ToOneField(SchemeLinkResource, 'link', full=True)
class Meta:
queryset = Scheme.objects.all()
resource_name = 'scheme'
Model:
class Participant(models.Model):
email = models.EmailField(unique=True)
mobile = PhoneNumberField(null=True, blank=True)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
schemes = models.ManyToManyField(Scheme)
You must use brackets such as
[]
around your schemes items (even if singular) when posting to a m2m field.
Request would then look like :
{"email":"test#test.com", "schemes":[{"id":"12", "schemes"}]}
When you want to know what a request should look like, make a GET request on url/of/api/modelresource/schema/
If I recall correctly (and although you wrote "POST" in your request), PATCH request must have
{"objects": [...]}
enclosing the body.
EDIT :
Here's an example of what works for me :
Resources :
class VATCertificateResource(ModelResource):
class Meta:
queryset = VATCertificate.objects.all()
resource_name = 'vatcertificate'
authorization = Authorization()
class InterventionResource(ModelResource):
vatcertificates = fields.ToManyField('core.api.VATCertificateResource', 'vatcertificates',
related_name='intervention', null=True, blank=True, full=True)
Models :
class VATCertificate(Document):
intervention = models.ForeignKey(Intervention, related_name='vatcertificates', blank=True, null=True)
class Intervention(models.Model):
pass
Hope this helps,
Regards,
Related
In my case,I use JWT authentication, and when I create new "Post"(my model), I want automatically set author to user that request it.But when I do it, I got an error
{
"author": [
"This field is required."
]
}
I know,I'm not passing user, but I want to set it automatically, and I dont know how.
I just want to know how to avoid error, because when I pass value, that allows me to go ahead, the user is set automatically from context.
Serializer
class PostSerializer(FlexFieldsModelSerializer):
class Meta:
model = Post
fields = ('title','content',author','category','likedBy')
expandable_fields = {
'category': ('blogApi.CategorySerializer', {'many': True}),
'comments': ('blogApi.CommentSerializer', {'many': True}),
'likedBy': ('blogApi.LikedBySerializer', {'many': True}),
}
def create(self, validated_data):
user = self.context['request'].user
post = Post.objects.create(
author=user, title=validated_data['title'], content=validated_data['content'])
post.category.set(validated_data['category'])
return post
My create view
class PostCreate(generics.CreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
Model
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
category = models.ManyToManyField(Category, related_name='posts')
author = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
likedBy = models.ManyToManyField(User, related_name='posts', blank=True)
class Meta:
ordering = ['-created']
def __str__(self):
return self.title
and when I create
You can make author a read-only field, or if you're just using this serializer to create users and not retreive them. You can just remove 'author' from fields in the serializer meta.
Read-only field
from rest_framework import serializers
class PostSerializer(FlexFieldsModelSerializer):
author = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Post
fields = ('title','content','author','category','likedBy')
I'm creating this simple shopping API in Django REST.
Internally I'm using IDs for foreign key constraints, while guuids are brought to the outside world.
For the checkout procedure, the user provides a list of article IDs he is willing to purchase. The object in the POST data thus looks as follows:
{
assets: [
{
'product': 'd9d5044d-2284-4d15-aa76-2eee3675035b',
'amount': 4
},
....
]
}
I'm using the following ticket/asset models:
# Ticket
class Ticket(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='tickets', on_delete=models.CASCADE)
# Assets
class Asset(models.Model):
ticket = models.ForeignKey(Ticket, related_name='assets', on_delete=models.CASCADE)
stock_item = models.ForeignKey(Stock, related_name='stock_item', on_delete=models.SET_NULL, null=True)
amount = models.IntegerField(validators=[MinValueValidator(0)])
And the serializers look as follows:
# Asset serializer
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ('stock_item', 'amount')
# Ticket serializer
class TicketSerializer(WritableNestedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
assets = AssetSerializer(many=True)
class Meta:
model = Ticket
fields = ('uuid', 'owner', 'assets', )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
When posting an object of the type specified above, the following error is presented:
{"assets":[{"stock_item": ["Invalid type. Expected PK, received string"]}]}
Which I can't seem to solve, how do I instruct the serializer to use the uuid as the lookup value? I solved a similar problem on view-level earlier by using the lookup_field member, but that doesn't seem to solve it. Any suggestions?
Enter code here
If I have understood you correctly, a SlugRelatedField should be able to find the correct related object.
class AssetSerializer(serializers.ModelSerializer):
ticket = serializers.SlugRelatedField(
read_only=True,
slug_field='uuid',
queryset=Ticket.objects.all() # Might be redundant with read_only=True
)
class Meta:
model = Asset
fields = ('ticket', 'stock_item', 'amount')
Elaborating on #BjornW's comment:
class UUIDRelatedField(serializers.SlugRelatedField):
slug_field = 'uuid'
def __init__(self, **kwargs):
super().__init__(slug_field=self.slug_field, **kwargs)
def to_representation(self, obj):
return getattr(obj, self.slug_field).hex
I'm currently starting a simple Task App and I'm using Django 2.0.7, DRF 3.8.2 and drf-nested-routes 0.90.2
I have these models :
class Client(TimeStampedModel):
"""
This model describes a client for the railroader. It can be created by the manager in the back office
We have at least one internal Client, which is Seelk, for internal projects
"""
name = models.CharField(max_length=255, unique=True)
description = models.TextField(null=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return "{} : {}".format(self.name, self.description)
class Project(TimeStampedModel):
"""
This model represents a project for a client, which we are gonna track actions on
"""
client = models.ForeignKey(
'railroader.Client', on_delete=models.PROTECT, related_name='projects')
name = models.CharField(max_length=255, unique=True)
description = models.TextField(null=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return "{} for client {}".format(self.name, self.client.name)
So, following the documentation of drf-nested-routers, I set up my serializers like this :
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ("id", "name", "description", "is_active", "projects")
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ("id", "name", "description", "is_active")
And my viewsets like this :
class ClientViewset(viewsets.ModelViewSet):
serializer_class = ClientSerializer
permission_classes = (permissions.IsAuthenticated, AccountPermission)
def get_queryset(self):
queryset = Client.objects.all()
is_active = self.request.query_params.get("is_active")
if is_active:
queryset = queryset.filter(is_active=is_active)
return queryset
class ProjectViewset(viewsets.ModelViewSet):
serializer_class = ProjectSerializer
permission_classes = (permissions.IsAuthenticated, AccountPermission)
def get_queryset(self):
queryset = Project.objects.filter(client=self.kwargs["client_pk"])
is_active = self.request.query_params.get("is_active")
if is_active:
queryset = queryset.filter(is_active=is_active)
return queryset
And finally, my urls like so :
router = routers.SimpleRouter()
router.register(r"clients", viewsets.ClientViewset, base_name="clients")
projects_router = routers.NestedSimpleRouter(router, r"clients", lookup="client")
projects_router.register(r"projects", viewsets.ProjectViewset, base_name="projects")
urlpatterns = [
re_path(r"^", include(router.urls)),
re_path(r"^", include(projects_router.urls))
]
With this setup, I'm able to have the desired nested routes, but I can't have my routes to create a new object if I post on a nested route.
I've seen an issue on the github speaking about it, but as it was 2 years ago, I wonder if anyone knows how to do it.
Thanks in advance.
Found out I just forgot that returning an instance in DRF create method of the serializer would not create the object in base. At the end I have this serializer :
class ProjectSerializer(serializers.ModelSerializer):
def create(self, validated_data):
client = Client.objects.get(pk=self.context["view"].kwargs["client_pk"])
validated_data["client"] = client
return Project.objects.create(**validated_data)
class Meta:
model = Project
fields = ("id", "name", "description", "is_active")
I'm using Django rest framework 3.2.1, the GET is perfect, but POST does not work.
This is the Model:
class Sample(models.Model):
ownerBuilding = models.ForeignKey(Building)
coordinateX = models.IntegerField(default=0, help_text='X coordinate for this sampling point located in a building')
coordinateY = models.IntegerField(default=0, help_text='Y coordinate for this sampling point located in a building')
creation_Time = models.DateTimeField(auto_now_add=True)
description = models.TextField(null=True,
help_text='additional description for this sample point.')
class Meta:
unique_together = ('ownerBuilding', 'coordinateX','coordinateY')
def __str__(self):
return "Sample for building " + str(self.ownerBuilding)
The Serializer:
class SampleSerializer(serializers.HyperlinkedModelSerializer):
ownerBuilding = serializers.HyperlinkedRelatedField(many=False, read_only=True, view_name='building-detail')
class Meta:
model = Sample
fields = ('url', 'ownerBuilding', 'coordinateX', 'coordinateY', 'creation_Time','description')
The View:
class SampleList(generics.ListCreateAPIView):
queryset = Sample.objects.all()
serializer_class = SampleSerializer
permission_classes = (permissions.IsAuthenticated, IsTechniciansGroupOrReadOnly,)
def get_queryset(self):
queryset = Sample.objects.all()
ownerBuildingId = self.request.query_params.get('ownerBuildingId', None)
if ownerBuildingId is not None:
queryset = queryset.filter(ownerBuilding=ownerBuildingId)
return queryset
When I test the POST to this API with data:
{"ownerBuilding":"http://rest.xxxxxxxx.xyz:8090/buildings/1/","coordinateX":33,"coordinateY":44,"description":"5dfadfasdfsadf5"}
I always get this error:
{
"ownerBuilding": [
"This field is required."
]
}
anyone could help?
the http://rest.xxxxxxxx.xyz:8090/buildings/1/ exists.
[edit0]:
if I POST with:
{"coordinateX":33,"coordinateY":44,"description":"5dfadfasdfsadf5"}
I still get the same result.
Serializer fields are required by default.
Also, DRF ModelSerializer (and HyperlinkedModelSerializer) adds UniqueTogetherValidators for all model's unique_together constraints. This implicitly makes all fields in the constraint required, with exception for fields for which defaults are set. See doc.
ownerBuilding is read only on the serializer:
ownerBuilding = serializers.HyperlinkedRelatedField(many=False, \
read_only=True, view_name='building-detail')
but you don't provide a defualt nor set the value manually, so this field is treated as empty, hence the error message.
Either remove the read_only or set a default.
I'm building an API using Tastypie with Django and I've run into a bit of an issue.
I have a model called a Moment (basically a blog post, with a title and body text), and I want to be able to attach comments to it and retrieve them via the API. I'm using django.contrib.comments with Django 1.6.5 and Tastypie 0.11.1.
Now, according to the Tastypie documentation, this should be straightforward. What I've implemented is pretty close to that. This is my models.py:
class Moment(models.Model):
"""
Represents a Moment - a statement by a user on a subject
"""
ZONE_CHOICES = (
('Communication', 'Communication'),
('Direction', 'Direction'),
('Empathy', 'Empathy'),
('Flexibility', 'Flexibility'),
('Motivation', 'Motivation'),
('Ownership', 'Ownership'),
('Persistence', 'Persistence'),
('Reliability', 'Reliability'),
('Teamwork', 'Teamwork'),
)
STATUS_CHOICES = (
('Open', 'Open'),
('More Info', 'More Info'),
('Closed', 'Closed'),
)
title = models.CharField(max_length=200)
text = models.TextField()
datetime = models.DateTimeField(default=timezone.now())
zone = models.CharField(max_length=200,
choices=ZONE_CHOICES)
sender = models.ForeignKey(Student, blank=True, null=True, related_name="sender")
status = models.CharField(max_length=200,
default='Open',
choices=STATUS_CHOICES)
recipient = models.ForeignKey(Sponsor, blank=True, null=True, related_name="recipient")
comments = generic.GenericRelation(Comment, object_id_field='object_pk')
def save(self, *args, **kwargs):
"""
Override the save() method to set the recipient dynamically
"""
if not self.recipient:
self.recipient = self.sender.sponsor
super(Moment, self).save(*args, **kwargs)
def __unicode__(self):
return self.title
class Meta:
ordering = ["-datetime"]
And this is my api.py:
class MomentResource(BaseResource):
"""
Moment resource
"""
sender = fields.ForeignKey(StudentResource, 'sender', full=True, readonly=True)
comments = fields.ToManyField('myapp.api.CommentResource', 'comments', blank=True, null=True)
class Meta:
"""
Metadata for class
"""
queryset = Moment.objects.all()
resource_name = 'moment'
always_return_data = True
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
filtering = {
'zone': ALL,
}
class CommentResource(ModelResource):
"""
Comment resource
"""
moment = fields.ToOneField(MomentResource, 'moment')
class Meta:
queryset = Comment.objects.all()
resource_name = 'comments'
However, the comments always come back blank.
Now, I know that the model seems to be correct because in the Django shell, the following returns the comments on a Moment:
Moment.objects.all()[0].comments.all()
I think the problem is therefore in api.py, but I haven't been able to track it down. Can anyone see where I've gone astray?
Finally got it working with the following:
class MomentResource(BaseResource):
"""
Moment resource
"""
sender = fields.ForeignKey(StudentResource, 'sender', full=True, readonly=True)
comments = fields.ToManyField('myapp.api.CommentResource', 'comments', null=True, full=True)
class Meta:
"""
Metadata for class
"""
queryset = Moment.objects.all()
resource_name = 'moment'
always_return_data = True
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
filtering = {
'zone': ALL,
}
class CommentResource(BaseResource):
"""
Comment resource
"""
moment = fields.ToOneField(MomentResource, 'content_object')
class Meta:
queryset = Comment.objects.all()
resource_name = 'comments'
I'm pretty sure the issue was with returning the Moment object from CommentResource, which was resolved by changing the attribute to content_object.