I'm pretty new to django's rest framework and I built my first example API using the official tutorial here. But I have no idea how to consume this API's data into another app in the same project such its data can be rendered into HTML.
Suppose I create an API students for students(with their details) in a school. Now how do I use this API in the same project to display the number of students in schools and their details.
Most of the tutorials or explanations online are for third-party API's and I can't figure out how to proceed. Thanks in advance.
models.py
class Test(models.Model):
date = models.DateTimeField(auto_now_add=True)
test_name = models.CharField(max_length=200,default='simple blood test',blank=False)
subject = models.CharField(max_length=100,default='')
def __str__(self):
return self.test_name
class Person(models.Model):
tests = models.ManyToManyField(Test)
title = models.CharField(max_length=3,default="mr",blank=False)
name = models.CharField(max_length=50,default='',blank=False)
def __str__(self):
return self.name
views.py
class PersonList(generics.ListCreateAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
serializers.py
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = Test
fields = ('test_name','subject')
class PersonSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
tests = TestSerializer(many=True, read_only=True)
class Meta:
model = Person
fields = ('url','id','name')
This is my API definiton. I want to create another app to display data such list of all students and details about them etc.
You will have to hit your endpoints in your consuming view, the easiest way to do this is with the requests library. First install the library:
pip install requests
Then use it in your consuming view:
def consumer_view(request):
response = requests.get('http://your-url.com/your-endpoint')
# do what you need to do here
You can use response.json() to get the JSON response form your API as a Python dictionary. If you are just using ./manage.py runserver your URL will be:
http:localhost:8000/your-endpoint
or
http://192.168.0.1:8000/your-endpoint
This way of consuming an API is somewhat redundant if you are working completely within Django. It's often much easier to use the ORM in these cases. However if you are making the API to be available for outside use (either publicly or via API keys) then this approach makes sense.
Related
Hi I'm new to Django and the Django rest framework so my terminology may be off.
I'm trying to build an API that gives back a list of items from a model but filtered based on fields in another related model.
I'll provide my current view and serializer classes and models
class service(models.Model):
name = models.CharField(max_length=50)
vendor = models.CharField(max_length=50)
version = models.CharField(max_length=10)
registration_status = models.BooleanField(default=False)
class service_network(models.Model):
service = models.OneToOneField(
service,
related_name='network',
on_delete=models.CASCADE,
primary_key=True,
)
forwarded_port = models.CharField(max_length=50)
class ServiceNetworkSerializer(serializers.ModelSerializer):
class Meta:
model = service_network
fields = '__all__'
class ServiceSerializer(serializers.ModelSerializer):
network = ServiceNetworkSerializer()
class Meta:
model = service
fields = [
'id',
'name',
'vendor',
'version',
'registration_status',
'network',
]
class ServiceAPI(ModelViewSet):
queryset = service.objects.all()
serializer_class = ServiceSerializer
filterset_fields = '__all__'
Currently I can get back lists using a URL query string
{{baseUrl}}/engine/service?registration_status=true
What I want to do is something like this
{{baseUrl}}/engine/service/network?forwarded_port=8080
Which I would expect to give back a list of services where the related network field "forwarded_port" is equal to 8080.
Is there another way to query this API? Maybe using a POST with a body containing the query? If there something in the DOCS that I can read, I've tried to look through filtering and querysets but I wasn't able to find anything that would do this out of the box
I'm also new to stackoverflow and I've tried to keep my question short with as much relevant information so if there anything missing I'd be happy to edit my question
I was able to solve this using the following queryset override
def get_queryset(self):
if len(self.request.GET) > 0:
query_set = {}
for query in self.request.GET:
query_set[query] = self.request.GET.get(query)
return service.objects.filter(**query_set)
else:
return service.objects.all()
What this does is lets you filter fields without explicitly specifying what they are, in cases when you have many fields that need filtering. I also have to say as I'm not experienced with Django, I'm not sure what kind of errors this may bring up but its a hack that's worked for me. If I find this is really bad I'll come back and remove this.
Try this:
{{baseUrl}}/engine/service?network__forwarded_port=8080
Probably it works.
Doc: https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships-1
EDIT:
If the above answer doesn't work, you can change the ServiceApi class and filter by yourself:
class ServiceAPI(ModelViewSet):
def get_queryset(self):
if self.request.GET.get(network__forwarded_port)
return service.objects.filter(network__forwarded_port = self.request.GET.get(network__forwarded_port))
else:
return service.objects.all()
serializer_class = ServiceSerializer
filterset_fields = '__all__'
i am in a situation where i need to create a django api which on call from frontend gives a response fetched internally from some other source but not from serializers and models. Currently i am using django rest framework to create api like below
MODELS
# Create your models here.
class submission(models.Model):
# fixed fields to show on today panel/pending panel
email_id = models.CharField(max_length=200, primary_key=True)
email_sender = models.CharField(max_length=200, null=True, blank=True)
def __str__(self):
return self.email_id
SERIALIZERS
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = submission
fields = ('__all__')
VIEWS
class SAMPLE(viewsets.ModelViewSet):
queryset = submission.objects.all()
serializer_class = UserSerializer
URLS
router = DefaultRouter()
# user table
router.register('user_table_all_data', SAMPLE, basename='user_table_all_data')
and it works as charm , but in order to work with django rest framework i always need to call like this 1. Create model or use existing model, 2. Create serializers,3. Create Views,4. Create URLs,
But what if i dont want to use models say for example i want to show data from other source say which contains a sample json like below
sampleData = {"Name":"RAJNISH","Age":32,"ADDRESS":"India"}
so if my API is say 'sample/someid (not pk)'
returns response as
{"Name":"RAJNISH","Age":32,"ADDRESS":"India"}
how can i do this in django rest framework?
Use Function based views instead of Class based views
def sample(request):
return Response(data={"Name":"RAJNISH","Age":32,"ADDRESS":"India"})
I am using DRF to generate API for my django app.
I am using the ViewSet class and exposing API endpoints for most of my models at their own path.
I want to allow viewing of my Endpoint and TLSCertificate models at an /assets/ path. As they are both children of an Organisation entity, I want to allow the results to be filtered Organisation.
So far I have:
serializers.py
class AssetSerializer(serializers.Serializer):
endpoints = EndpointSerializer(many=True)
certificates = TLSCertificateSerializer(many=True)
views.py
class AssetFilterSet(filters.FilterSet):
organisation = filters.ModelChoiceFilter(
name='organisation', queryset=Organisation.objects.all())
project = filters.ModelChoiceFilter(
name='project', queryset=Project.objects.all())
class Meta:
model = Endpoint
fields = ['organisation', 'project']
# the object type to be passed into the AssetSerializer
Asset = namedtuple('Asset', ('endpoints', 'certificates'))
class AssetViewSet(CacheResponseAndETAGMixin, viewsets.ViewSet):
"""
A simple ViewSet for listing the Endpoints and Certificates in the Asset list.
Adapted from https://stackoverflow.com/questions/44978045/serialize-multiple-models-and-send-all-in-one-json-response-django-rest-framewor
"""
# TODO filtering not functional yet
filter_class = AssetFilterSet
filter_fields = ('organisation', 'project',)
queryset = Endpoint.objects.all()
def list(self, request):
assets = Asset(
endpoints=Endpoint.objects.all(),
certificates=TLSCertificate.objects.all(), )
serializer = AssetSerializer(assets, context={'request': request})
return Response(serializer.data)
This works in returning the objects but does not allow for filtering to take place.
I would appreciate any guidance in how to enable the filtering in this situation?
I have a few apps within my Django project. There are two apps that I am currently working with "Application" and "User" and I have two questions related to models:
Question 1:
I want to design it in such a way so that external users submit their contact form on Application/templates/Application/Apply.html and the info would get added to the database. Internal users would be able to add external users as well but from a different template: User/templates/User/AddNewContact.html
I am able to add a new contact from an internal user's perspective:
User/models.py
class Contact(models.Model):
ContactName = models.CharField(max_length = 250, default='')
ContactResidence = models.CharField(max_length = 250, default='')
Tel = models.CharField(max_length = 250, default='')
def get_absolute_url(self):
return reverse('User:ContactDetails', kwargs={'pk': self.pk}
)
def __str__(self):
return self.ContactName
class Locations(models.Model):
contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
Country = models.CharField(max_length=250, default='')
def __str__(self):
return self.Country
I was going to just copy this model and paste it into Application/models.py but there are two problems:
1) I don't want external users to be directed to URL: User:ContactDetails and technically, it is not going to work out because I will build the authentication later on.
2) I feel that by copying and pasting I am breaking the 'don't repeat yourself" rule.
Should I connect two models using the foreign keys? What are the best practices in this case?
Question 2
Am I working with one-to-many relationship according to the model provided? I want to have one contact with his personal info (tel/email/address) and a number of branch locations across the world associated with that contact.
To be used a relationship one to many, you can be doing as after:
On models of father app (father table):
class Department(models.Model):
dept_id = models.AutoField(primary_key=True)
On models of child app (child table):
from appname.models import Department
class Office(models.Model):
office_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
dept = models.ForeignKey(Department, on_delete=models.CASCADE)
It helped me.
Question 1: Well, you don't need to copy paste the model. You can use models from other django apps anytime, just need to import it. Basically what you should do is, instead of linking the url directly to the template in the Applications app, you should connect it to a view. In the view file you can import the models from User.models import *, and use them normally.
Question 2: As far as I understand the question your structure provides what you want: one contact (with personal info) associated with several countries. Except that you should replace Agent by Contact in contact = models.ForeignKey(Agent, on_delete=models.CASCADE)
Question 1: Note that the 'get_absolute_url' method is only called if you don't provide a success url in your view. If you are using a CreateView or FormView you can specify the success url by overriding the get_success_url method, for example:
class ContactCreateView(CreateView):
model = Contact
fields = ['ContactName', 'ContactResidence', 'Tel']
def get_success_url(self):
if not self.request.user.internal: # e.g. internal is a User bool field
return HttpResponseRedirect('some/external/url/')
return super().get_success_url() # call get_absolute_url model method.
The DRY principle is respected.
Question 2: Yes, the question you need to ask yourself is 'does a model instance (In this case Contact) have many instances of another model (Location)?' If the answer is yes, then the M2M field should go into your Contact model. See the django docs explaining the pizza/toppings example.
The apps should be in the same project and you can import one model as:
import appName.models or
from appName.models import ClassName
In app2 models you can use foreignKey or manyTomany after importing the class:
from appsName.models import ClassName
class Person(models.Model):
con = ForeignKey(ClassName)
I have three models — articles, authors and tweets. I'm ultimately needing to use Django REST Framework to construct a feed that aggregates all the objects using the Article and Tweet models into one reverse chronological feed.
Any idea how I'd do that? I get the feeling I need to create a new serializer, but I'm really not sure.
Thanks!
Edit: Here's what I've done thus far.
app/serializers.py:
class TimelineSerializer(serializers.Serializer):
pk = serializers.Field()
title = serializers.CharField()
author = serializers.RelatedField()
pub_date = serializers.DateTimeField()
app/views.py:
class TimelineViewSet(viewsets.ModelViewSet):
"""
API endpoint that lists all tweet/article objects in rev-chrono.
"""
queryset = itertools.chain(Tweet.objects.all(), Article.objects.all())
serializer_class = TimelineSerializer
It looks pretty close to me. I haven't used ViewSets in DRF personally, but I think if you change your code to this you should get somewhere (sorry - not tested either of these):
class TimelineViewSet(viewsets.ModelViewSet):
"""
API endpoint that lists all tweet/article objects in rev-chrono.
"""
def list(self, request):
queryset = list(itertools.chain(Tweet.objects.all(), Article.objects.all()))
serializer = TimelineSerializer(queryset, many=True)
return Response(serializer.data)
If you're not wedded to using a ViewSet then a generics.ListAPIView would be a little simpler:
class TimeLineList(generics.ListAPIView):
serializer_class = TimeLineSerializer
def get_queryset(self):
return list(itertools.chain(Tweet.objects.all(), Article.objects.all()))
Note you have to convert the output of chain to a list for this to work.