Is it possible to POST a new object while also specifying its id instead of auto incrementing? This is a one time import and the database id sequence would be corrected afterward.
class TestModelSerializer(serializers.ModelSerializer):
class Meta:
model = TestModel
fields = ('id', 'name')
class TestModelViewSet(viewsets.ModelViewSet):
queryset = TestModel.objects.all()
serializer_class = TestModelSerializer
import requests
def test_post(endpoint):
data = {
"id": 30,
"name": "Test",
}
r = requests.post(endpoint, data=data)
print(r.status_code, r.reason)
test_post('http://localhost:8000/api/test_model/30/')
>>> 405 Method Not Allowed
test_post('http://localhost:8000/api/test_model/')
>>> 201 Created
201 Created creates a new object but with the next id in sequence instead of the desired id.
I've also tried r = requests.put('http://localhost:8000/api/test_model/30/', data=data) but get 404 Not Found
This could work but i'm not sure, I believe django-rest is reading from your django model that id is an auto-increment field, and hence doesn't create with the id provided. But it could work if you specify it explicitly as an integer field.
class TestModelSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
class Meta:
model = TestModel
fields = ('id', 'name')
Related
I'm trying to return the name of the pricing field but all I get is its foreign key id instead. What am I doing wrong here? I looked at some similiar issues on here but I didn't find anything that resembled my situation.
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
"assignedteams",
"agent",
"facility",
"organisor",
"avatar",
)
class UserSubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = (
"user",
"pricing",
"status",
)
class UserSerializer(UserDetailsSerializer):
profile = UserProfileSerializer(source="userprofile")
subscription = UserSubscriptionSerializer(source="usersubscription")
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('profile', 'subscription',)
def update(self, instance, validated_data):
userprofile_serializer = self.fields['profile']
userprofile_instance = instance.userprofile
userprofile_data = validated_data.pop('userprofile', {})
usersubscription_serializer = self.fields['subscription']
usersubscription_instance = instance.usersubscription
usersubscription_data = validated_data.pop('usersubscription', {})
# update the userprofile fields
userprofile_serializer.update(userprofile_instance, userprofile_data)
usersubscription_serializer.update(usersubscription_instance, usersubscription_data)
instance = super().update(instance, validated_data)
return instance
You have 2 options to solve this problem.
option1:
If you want to return only the name of your pricing model you can use SlugRelatedField to do it.
Example:
class UserSubscriptionSerializer(serializers.ModelSerializer):
pricing = serializers.SlugRelatedField('name', readonly=True)
class Meta:
model = Subscription
fields = (
"user",
"pricing",
"status",
)
Option2:
If you want to return the Pricing object you can create a new ModelSerializer for your Pricing model and use it.
Example:
class PricingSerializer(serializers.ModelSerializer):
class Meta:
model = Pricing
fields = ["id","name"]
class UserSubscriptionSerializer(serializers.ModelSerializer):
pricing = PricingSerializer(readonly=True)
class Meta:
model = Subscription
fields = (
"user",
"pricing",
"status",
)
There are some other options that can you use but you must explain more about your problem can I will help you with.
you can easily add a new field representation or override the pricing field when want to represent data
so in your serializer add the following code
class UserSubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = (
"user",
"pricing",
"status",
)
def to_representation(self, instance):
data = super().to_representation(instance)
data['pricing_name'] = instance.pricing.name # or replace the name with your pricing name field
return data
As you are saying pricing returned FK id, so i assume pricing column inside Subscription model is a FK to another model, let's assume it Pricing model.
You can create a serializer for Pricing and use it on UserSubscriptionSerializer,
like the way you created UserProfileSerializer and UserSubscriptionSerializer for UserSerializer
But, using directly a nested serializer will give you problem while doing write operation since as far i can understand you are accepting pricing as FK value when creating or updating
To solve this issue you can do some if/else on get_fields() method
class UserSubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = (
"user",
"pricing",
"status",
)
def get_fields(self):
fields = super().get_fields()
# make sure request is passed through context
if self.context['request'] and self.context['request'].method == 'GET':
fields['pricing']=PricingSerializer()
return fields
Now coming back to the question, since you only need the pricing name which i assume name is a column on Pricing model
simply rewrite the previous code as
def get_fields(self):
fields = super().get_fields()
# make sure request is passed through context
if self.context['request'] and self.context['request'].method == 'GET':
fields['pricing'] = serializers.CharField(source='pricing.name', read_only=True)
return fields
P.S: I haven't tested this code on my computer
Using the Django REST Framework 2.2, I have a Person model as follows in models.py::
class Person(models.Model):
id = models.CharField(max_length = 20, primary_key = True, blank = True)
name = Models.CharField(max_length = 1024, blank = True)
values = {}
#staticmethod
def create_person(personData):
person = Person(
name = personData.get("name", "Unknown"),
values = personData.get("values", {}),
)
return person
All data is stored in a Firestore database for saving and retrieving data via the REST API. Before new entries are made into the database, a serializer is used to validate incoming POST data.
The route /person takes POST request data and runs it by the PersonCreateSerializer in
views.py:
def create_person(request):
"""
Route: /person
Method: POST
"""
try:
print(request.data)
# Above print outputs:
# <QueryDict: {'name': ['John Doe'], 'values': ['{ "height": 180 }']}>
serializer = PersonCreateSerializer(data = request.data)
serializer.is_valid(raise_exception = True)
person = Person.create_person(request.data)
...
except APIException as exception:
return JsonResponse(exception.APIError, status = exception.status)
serializers.py:
class PersonCreateSerializer(CreateModelSerializer):
class Meta:
model = Person
fields = "__all__"
def validate(self, data):
print(data)
# Above print outputs:
# OrderedDict([('name', 'John Doe')])
# Notice missing 'values' field.
if not data.get("values"): # Ensure we have a values field within the data.
raise APIException("ERROR_MISSING_FIELD", "Missing required field 'values'.", 400)
return data
The problem is however any value provided for the values dictionary is discarded when the serializer validate() function receives it.
POST Payload:
My question is why is the dictionary received from the POST request not received by the serializer so it can be parsed? What is the correcy way to create dictionary fields in Django?
Sent to Serializer:
<QueryDict: {'name': ['John Doe'], 'values': ['{ "height": 180 }']}>
Received by Serializer:
OrderedDict([('name', 'John Doe')])
The problem with JSONField and HStoreField
I have looked at alternatives mentioned such as HStoreField and JSONField however this data is being stored in a Firestore database and the key-value association needs to be retained rather than it being stored as a plain JSON string.
Because the data is being stored in Firestore, the structure of the dictionary array needs to be retained as a map in the database, this allows it to be indexed and queried with Firestore queries.
If we use JSONField, this simply converts the value to a string and removes this functionality.
I believe it is because values is not a field. It is just a class variable.
Serializer filters the data by fields you have pointed to. And you pointed to __all__ which means all fields in model.
You can try:
fields = ['name', 'values']
And if it didn't work, make a function and pass it "as a field":
# models.py
class Person(models.Model):
id = models.CharField(max_length = 20, primary_key = True, blank = True)
name = Models.CharField(max_length = 1024, blank = True)
values = {}
def get_values(self):
return self.values
# serializers.py
class PersonCreateSerializer(CreateModelSerializer):
class Meta:
model = Person
fields = ['name', 'get_values']
The solution I found was to make use of the django-dictionaryfield module, this provides a Dictionary field type that can be used for converting to and from all array types, such as dictionaries and lists.
Without a field declared in the model.py, the serializer ignores it since it isn't considered part of the model itself, therefore using a custom DictionaryField model allows it to be stored as a Django model field.
Django DictionaryField Setup
Install the module into your project:
$ pip install django-dictionaryfield
Add dictionaryfield into your INSTALLED_APPS in the Django configuration file:
INSTALLED_APPS = (
...
"dictionaryfield",
)
Model Class
Use the DictionaryField for fields that should be arrays.
from django.db import models
from dictionaryfield import DictionaryField
class Person(models.Model):
id = models.CharField(max_length = 20, primary_key = True, blank = True)
name = Models.CharField(max_length = 1024, blank = True)
values = DictionaryField(default = {})
#staticmethod
def create_person(personData):
person = Person(
name = personData.get("name", "Unknown"),
values = personData.get("values", {}),
)
return person
I have 3 models: Maker, Item and MakerItem that creates the relation between the items and their makers:
class Maker(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=100)
class Item(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=100)
class MakerItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
item_id = models.ForeignKey(Item, on_delete=models.CASCADE)
maker_id = models.ForeignKey(Maker, on_delete=models.CASCADE)
the items can have a random amount of makers.
I want to create both the Item and the MakerItem objects at the same time with a single set of data,
for example if a Maker with id = "abcd" already exists, and I go to /item and send a POST request with the following data:
{
"name": "item1",
"makers": [
{
"maker_id": "abcd"
}
]
}
I want the serializer to create the Item object and the MakerItem object.
I have achieved this, with the following setup:
views.py
class ItemListCreate(ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class MakerItemSerializer(serializers.ModelSerializer):
class Meta:
model = MakerItem
exclude = ['id', 'item_id']
makers = MakerItemSerializer(many=True)
class Meta:
model = Item
fields = ['id', 'name', 'makers']
def create(self, validated_data):
maker_item_data = validated_data.pop('makers')
item_instance = Item.objects.create(**validated_data)
for each in maker_item_data:
MakerItem.objects.create(
item_id=check_instance,
maker_id=each['maker_id']
)
return item_instance
but when Django tries to return the created object, it always gives me the error:
AttributeError at /item/
Got AttributeError when attempting to get a value for field `makers` on serializer `ItemSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Item` instance.
Original exception text was: 'Item' object has no attribute 'makers'.
What am I doing wrong?
Thanks
EDIT: To clarify, the objects get created and populate the database correctly, but when the browsable API that DRF provides tries to display the created object, it gives me the error above.
Change:
class ItemSerializer(serializers.ModelSerializer):
class MakerItemSerializer(serializers.ModelSerializer):
class Meta:
model = MakerItem
exclude = ['id', 'item_id']
makers = MakerItemSerializer(many=True)
To:
class ItemSerializer(serializers.ModelSerializer):
class MakerItemSerializer(serializers.ModelSerializer):
class Meta:
model = MakerItem
exclude = ['id', 'item_id']
makers = MakerItemSerializer(many=True, source="makeritem_set")
Hope this works!
For clarity, you're attempting to serialise the reverse relationship between MakerItem and Item for this serialiser.
This means that the attribute on your object is automatically set by Django as fieldname_set but you can override this behaviour by setting the related_name kwarg on the field and then makemigrations and migrate it.
In your case you would need to do:
maker_id = models.ForeignKey(Maker, on_delete=models.CASCADE, related_name="maker_items")
And then update the field in the Meta to match the new field name, this way you don't have to manually specify source. Because actually the attribute "makers" is misleading, due to the fact its actually the MakerItem, not the Maker itself.
See https://docs.djangoproject.com/en/3.2/ref/models/relations/ for further details about this behaviour.
I want to create a serializer that uses the variables from my model and also counts how many data of the same id is found in the table.
I have created this, but it doesn't work:
class WebsiteSerializer(serializers.Serializer):
item_nr = serializers.IntegerField()
class Meta:
model = URL
fields = (
"id",
"item",
"status",
"item_nr "
)
def get_item_nr (self, obj):
obj.item_nr = Items.objects.filter(item_id=self.context.get(id)).count()
return obj.item_nr
This is the error that I get:
Got AttributeError when attempting to get a value for field item_nr
on serializer WebsiteSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the URL instance.
Original exception text was: 'URL' object has no attribute 'item_nr '.
items_nr will be a SerializerMethodField not IntegerField
The field will be automatically assigned data type based on what you return in get_item_nr.
class WebsiteSerializer(serializers.Serializer):
item_nr = serializers.SerializerMethodField()
class Meta:
model = URL
fields = (
"id",
"item",
"status",
"item_nr "
)
def get_item_nr (self, obj):
obj.item_nr = Items.objects.filter(item_id=self.context.get(id)).count()
return obj.item_nr
I have a problem where I want to filter model resourse based on a field and show that particular queryset to the requesting client_id.
I am using tastypie v0.10.0 with django 1.6.5 and swagger UI for documentation.
In Example model I have stored information related to all clients and want to show the data belongs to the particular client based on client id. I have filters fields in Example model based on which I can create queryset for particular client.
class Resource(ModelResource):
class Meta:
queryset = Example.objects.all()
resource_name = 'example'
authorization = DjangoAuthorization()
detail_allowed_methods = ['get',]
authentication = OAuth20Authentication()
Please suggest me the best way to implement the above scenario. Thanks in advance.
I think of two possibilities, how to filter you resources based on client_id. I suppose client_id is one field of your model:
Or you can use tastypie filtering:
class Resource(ModelResource):
class Meta:
queryset = Example.objects.all()
resource_name = 'example'
authorization = DjangoAuthorization()
detail_allowed_methods = ['get',]
authentication = OAuth20Authentication()
filtering = {
'client_id': 'exact',
}
This time, you should pass filtering parameter by GET method, something like:
http://website.com/api/?client_id=5
Or you can write your own obj_get_list method::
class Resource(ModelResource):
class Meta:
queryset = Example.objects.all()
resource_name = 'example'
authorization = DjangoAuthorization()
detail_allowed_methods = ['get',]
authentication = OAuth20Authentication()
def obj_get_list(self, bundle, **kwargs):
queryset = Message.objects.filter(client_id = bundle.request.GET['client_id'])
return queryset
Here is the solution I came out with.
import provider.oauth2
from provider.oauth2.models import AccessToken
class Resource(ModelResource):
#
class Meta:
queryset = Example.objects.all()
resource_name = 'example'
authorization = DjangoAuthorization()
detail_allowed_methods = ['get',]
always_return_data = True
authentication = OAuth20Authentication()
key = bundle.request.GET.get('oauth_consumer_key')
if not key:
key = bundle.request.POST.get('oauth_consumer_key')
if not key:
auth_header_value = bundle.request.META.get('HTTP_AUTHORIZATION')
if auth_header_value:
key = auth_header_value.split(' ')[1]
token = verify_access_token(key)
>>>>> token.user this contains username
>>>> come out with the proper conditions through which you can create queryset
queryset = Example.objects.filter(field=condition)
return queryset