I found a documentation since it is not working with python updated version so I am having this problem. I want to prevent scrapping from my application. There are some api where I am passing sensitive data and my api endpoing is like localhost:8000/api/products/1 but I want this url to be like
localhost:8000/api/products/dheudhuehdeidiwf4yfg4gfy4yf4f4fu4f84j4i this. So which procedure should I follow here?
You can use uuid as another unique key in your model.
import uuid
class Product(models.Model):
uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
# other fields ...
For the serializers, you'll have to manually set them like:
class ProductSerializer(serializers.Serializer):
uuid = serializers.UUIDField(format="hex", read_only=True)
# other fields ...
class Meta:
model = Product
fields = [
"uuid",
# other fields ...
]
For the views, I'm assuming you are using ModelViewSet, so you can set the uuid as the lookup field like:
class ProductViewSet(viewsets.ModelViewSet):
serializer_class = ProductSerializer
lookup_field = "uuid"
One way to go about making your sensitive ids urlsafe would be to use urlsafe_base64_encode from django.utils.http. You could return encrypted ids along with your response to the frontend using:
uidb64 = urlsafe_base64_encode(force_bytes(model_name.pk))
the frontend can then persist the encrypted ids and when request are made to your endpoints using those ids, you then decrypt them using smart_str from django.utils.encoding like this:
model_name_pk = smart_str(urlsafe_base64_decode(uidb64))
assuming your endpoints looks something like this 'api/an-interesting-route/<uidb64>'
This approach is more useful with GET endpoints that do not just return the model directly but include some amount of processing of the id before a response is returned.
Related
I wanna change all fields of a json object except 'pk' in DRF. I just need to keep one json data. When adding a new data ,this one should override existing data. Is there a way to do it with django ?
my models.py
class ClientUser2(models.Model):
phone_number = models.CharField(max_length=20,unique=True)
name = models.CharField(max_length=100,blank=True)
status = models.IntegerField(default=1)
class ClientNameSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ClientUser2
fields = ('url','phone_number','name','status','pk')
my views.py
class ClientViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows messages to be viewed or edited.
"""
queryset = ClientUser2.objects.all()
serializer_class = ClientNameSerializer
and it's my api root
api_root
If you want to be able to only retrieve and update models you can use RetrieveUpdateApiView
Reference : https://www.django-rest-framework.org/api-guide/generic-views/#retrieveupdateapiview
I'm using Django REST Framework with the HyperlinkedModelSerializer
serializer.py:
class ReportTypesViewSet(viewsets.ModelViewSet):
queryset = ReportType.objects.all()
serializer_class = ReportTypesSerializer
api.py:
class ReportTypesSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ReportType
fields = ('name', 'uuid', 'active', )
Now the API works, but the hyperlinks have the pk within their URLs, such as: http://localhost:8000/api/reporttypes/1/
I would like to map the objects via the UUID field (which the model provides) rather than the internal PK. I know I could change the primary key to the UUID field, but I've read that would cause other issues, such as decreased performance.
Is there a way I can reference the objects via the UUID, but still internally use the default pk (id)?
This is what you should do:
class ReportTypesViewSet(viewsets.ModelViewSet):
queryset = ReportType.objects.all()
serializer_class = ReportTypesSerializer
lookup_field = 'uuid'
This tells DRF that you are using the uuid field for lookups and not the pk which is the default
For your url config use slug instead pk:
path(_('api')+'/<slug:slug>/', app.api_view, name='api'),
For your model/serializer make sure it contains both uuid and id
I am trying to set a series of Django Rest Framework URLs.
Below is my Serializer/ViewSet makeup
class ModelSerializer(serializers.HyperlinkedModelSerializer):
schemas = SchemaSerializer(many=True, read_only=True)
class Meta:
model = dbModels
fields = ('ModelName', 'pk', 'schemas')
class ModelViewSet(viewsets.ModelViewSet):
queryset = dbModels.objects.all()
serializer_class = ModelSerializer
class ModelListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = dbModels
fields = ('ModelName', 'pk')
class ModelListViewSet(viewsets.ModelViewSet):
queryset = dbModels.objects.all()
serializer_class = ModelListSerializer
Here is my Router List:
from datagenerator.serializer import UserViewSet, \
ModelViewSet, ModelListViewSet
router = routers.DefaultRouter()
router.register(r'models', ModelViewSet)
router.register(r'modellist', ModelListViewSet)
However, when I'm running The localhost webserver, the DRF Front end shows this:
"models": "http://localhost:8000/datamaker/api/modellist/",
"modellist": "http://localhost:8000/datamaker/api/modellist/",
How do I stop this?
I need models to go to models and modellist to go to modellist.
Thanks much...
Use the base_name argument:
router.register(r'models', ModelViewSet, base_name='models')
router.register(r'modellist', ModelListViewSet, base_name='modellist')
Since your serializers share the same data model, DRF might get stuck trying to automatically discover the url naming pattern. So it's better in this case to explicitly set the base_name.
If you're using a newer version of Django Rest Framework, you'll need to use basename='models' instead of base_name='model'.
I am working on a Django REST framework API and are running into some issues with using the Serializers. I'll try to isolate the problem a little. Basically there are the Room and a Location models.
class Room(models.Model):
uuid = UUIDField(primary_key=True)
...
location = models.ForeignKey(Location, related_name='room')
...
class Location(models.Model):
uuid = UUIDField(primary_key=True)
name = models.CharField(max_length=255)
def __unicode__(self):
return unicode(self.name)
and the corresponding serializers,
class RoomSerializer(serializers.ModelSerializer):
location = serializers.RelatedField()
class Meta:
model = Room
fields = ('uuid', 'location')
class LocationSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Location
fields = ('uuid', 'name')
I've explored the many ways to change the display of uuid to the location name including the use of serializers.SerializerMethodField() and also using the above serializers.RelatedField(), and they do work. As a reference, see the JSON output below.
{
"uuid": "491ab09d-qqqq-wwww-eeee-5801dbac0fef",
"location": "Kota Kinabalu",
"created": "2014-09-03T07:52:45.399Z",
"modified": "2014-09-03T07:52:45.530Z"
}
However, I am not able to do a model.save() on Room since Django or rather the database (postgresql in my case) complains that the location_id is empty. I think its due to the fact that when I send a POST request to the API endpoint /api/rooms/ the location_id is left blank when I modify my RoomSerializer.
If one uses a default ModelViewSet, you would see that the Location field is missing from the default form.
How do I use serializers properly such that I could display the name of the location ("Kota Kinabalu" instead of a uuid string like 6e6acbbb-xxxx-yyyy-zzzz-1cf1a5bac22c) and I am still able to accept a correct POST request?
(I'm moving quik_silv comment into an answer)
You should use the SlugRelatedField provided by DRF: http://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield
In this case all you have to do is add a field like: location = serializers.SlugRelatedField(slug_field='name', read_only=True). Note the use of the parameter read_only, which should fix the problem in creating model instances.
You should try using customized UUID related field. You can find an example here.
I know it had been a long time and you might have already got the solution. But I am posting here the solution how I implemented this. After reading a bit of docs, I came across a method named to_representation(). I used this method to convert the data which is being returned in form of primary key to any other form. You can refer about this method in the docs here.
views.py
I'm creating a queryset that I want to serialize and return as JSON. The queryset looks like this:
all_objects = Program.objects.all()
test_data = serializers.serialize("json", all_objects, use_natural_keys=True)
This pulls back everything except for the 'User' model (which is linked across two models).
models.py
from django.db import models
from django.contrib.auth.models import User
class Time(models.Model):
user = models.ForeignKey(User)
...
class CostCode(models.Model):
program_name = models.TextField()
...
class Program(models.Model):
time = models.ForeignKey(Time)
program_select = models.ForeignKey(CostCode)
...
Question
My returned data has Time, Program, and CostCode information, but I'm unable to query back the 'User' table. How can I get back say the 'username' (from User Table) in the same queryset?
Note: I've changed my queryset to all_objects = Time.objects.all() and this gets User info, but then it doesn't pull in 'CostCode'. My models also have ModelManagers that return the get_by_natural_key so the relevant fields appear in my JSON.
Ultimately, I want data from all four models to appear in my serialized JSON fields, I'm just missing 'username'.
Here's a picture of how the JSON object currently appears in Firebug:
Thanks for any help!
It seems a bit heavyweight at first glance but you could look at using Django REST Framework:
http://www.django-rest-framework.org/api-guide/serializers#modelserializer
You can define and use the serializer classes without having to do anything else with the framework. The serializer returns a python dict which can then be easily dumped to JSON.
To get all fields from each related model as nested dicts you could do:
class ProgramSerializer(serializers.ModelSerializer):
class Meta:
model = Program
depth = 2
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
To customise which fields are included for each model you will need to define a ModelSerializer class for each of your models, for example to output only the username for the time.user:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', )
class TimeSerializer(serializers.ModelSerializer):
"""
specifying the field here rather than relying on `depth` to automatically
render nested relations allows us to specify a custom serializer class
"""
user = UserSerializer()
class Meta:
model = Time
class ProgramSerializer(serializers.ModelSerializer):
time = TimeSerializer()
class Meta:
model = Program
depth = 1 # render nested CostCode with default output
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
What you really want is a "deep" serialization of objects which Django does not natively support. This is a common problem, and it is discussed in detail here: Serializing Foreign Key objects in Django. See that question for some alternatives.
Normally Django expects you to serialize the Time, CostCode, Program, and User objects separately (i.e. a separate JSON array for each) and to refer to them by IDs. The IDs can either be the numeric primary keys (PKs) or a "natural" key defined with natural_key.
You could use natural_key to return any fields you want, including user.username. Alternatively, you could define a custom serializer output whatever you want there. Either of these approaches will probably make it impossible to load the data back into a Django database, which may not be a problem for you.