I am using Django 3.0.4 with Django REST Framework 3.11.0 where I have a models.py like:
from django.db import models
from model_utils.models import TimeStampedModel
class SampleModel(TimeStampedModel):
class Options(models.TextChoices):
FOO = "A"
BAR = "B"
BAZ = "C"
name = models.CharField(default="", max_length=512)
options = models.CharField(
max_length=2,
choices=Options.choices,
default=Options.FOO
)
I would like to be able to create an API endpoint to return a list of my TextChoices as a tuple. I have a React frontend where I want to create a <select> dropdown with my list of choices. If I can access the list of TextChoices via an API endpoint, I should be good to go.
path("api/sample/choices/", views.SampleChoicesListView.as_view(), name="sample_choices")
I'm not sure what my views.py needs to look like to make this work...
class SampleChoicesListView(generics.ListAPIView):
pass
Return the SampleModel.Options.choices as Response in an APIView
from rest_framework.views import APIView
from rest_framework.response import Response
# other required imports
class SampleChoicesListView(APIView):
def get(self, request):
# SampleModel.Options.choices would return
# [('A', 'Foo'), ('B', 'Bar'), ('C', 'Baz')]
choices = SampleModel.Options.choices
# format it into dictionary
# choices = {choice[0]: choice[1] for choice in SampleModel.Options.choices}
return Response(choices)
Add urlpattern to api urls
path("options/", views.SampleChoicesListView.as_view(), name="options"),
List structure:
Dictionary structure:
It may be cleaner to instead make an OPTIONS request to the SampleModel endpoint which should enumerate the valid options for that field, and also saves you creating a separate endpoint just to list the options. DRF should handle this all for you.
Related
I want to create a new user's login and random/same password using CSV file using the ImportExport module Django. I've tried many examples but for no use. Can you please suggest anything? I've even tried to make password field in the import csv file. Let me know any tutorial/link that you can share to achieve this.
If you're looking to populate the database with dummy data, there is a module called faker in python.
Here is a small code snippet showing how you can use faker:
import os
# Configure settings for project
# Need to run this before calling models from application!
os.environ.setdefault('DJANGO_SETTINGS_MODULE','(YOUR DJANGO PROJECT).settings')
import django
# Import settings
django.setup()
import random
from (YOUR DJANGO APP).models import User
from faker import Faker
fakegen = Faker()
def populate(N=5):
for entry in range(N):
# Create Fake Data for entry
fake_name = fakegen.name().split()
fake_first_name = fake_name[0]
fake_last_name = fake_name[1]
fake_email = fakegen.email()
# Create new User Entry
user = User.objects.get_or_create(first_name=fake_first_name,
last_name=fake_last_name,
email=fake_email)[0]
if __name__ == '__main__':
print("Populating the databases...")
populate(20)
print('Populating Complete')
django-import-export contains several hooks which you can use to manipulate data prior to insertion in the db. This seems like the ideal way to generate a random password for each user.
You can combine this with Django's make_random_password() function. It doesn't seem like a good idea to have passwords in the import spreadsheet, but maybe you need to do that for some reason.
You will need to create your own Resource subclass, and override the before_save_instance(), for example:
class UserResource(resources.ModelResource):
def before_save_instance(self, instance, using_transactions, dry_run):
pw = User.objects.make_random_password()
instance.set_password(pw)
class Meta:
model = User
fields = ('id', 'username', 'password')
import_id_fields = ['id']
I am using django-hitcount and am stumped on counting hits within the views.py for my app. The documentation suggests using this
from hitcount.models import HitCount
from hitcount.views import HitCountMixin
# first get the related HitCount object for your model object
hit_count = HitCount.objects.get_for_object(your_model_object)
# next, you can attempt to count a hit and get the response
# you need to pass it the request object as well
hit_count_response = HitCountMixin.hit_count(request, hit_count)
# your response could look like this:
# UpdateHitCountResponse(hit_counted=True, hit_message='Hit counted: session key')
# UpdateHitCountResponse(hit_counted=False, hit_message='Not counted: session key has active hit')
However, I am not looking for responses to a HitCount object. I only want to count the hits in a similar manner to how the template tags provide, like this
{% get_hit_count for [object] within ["days=1,minutes=30"] %}
How exactly do I get the hit count for an object in a given timeframe inside of the views.py ?
I had the same predicament. So I looked into their views and implemented what you are asking in a function-based view. Let's pretend we want to count all views on a user's profile. The view:
import hitcount # import entire package
from hitcount.models import HitCount
def profile(request):
my_profile = Profile.objects.get(user=request.user)
# mark it
hit_count = HitCount.objects.get_for_object(my_profile)
hit_count_response = hitcount.views.HitCountMixin.hit_count(request, hit_count)
# count the views
profile_views = my_profile.hit_count.hits
return render( request, 'profile.html', locals() )
I found that I had to import entire hitcount package to work with hitcount.views.HitCountMixin. I guess my app was confusing hitcount.views.HitCountMixin with hitcount.models.HitCountMixin probably because I had imported everything from my models.py. So, if in your views.py you have from .models import * like me, it is wise to import entire package to specify which HitCountMixin you are referring to... Just like in the code above...
Anyway, ensure that the model you want to count hits inherits from hitcount.models.HitCountMixin and that you have a generic relationship to hitcount.models.HitCount) e.g.
from hitcount.models import HitCountMixin, HitCount
class Profile(models.Model, HitCountMixin):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
hit_count_generic = GenericRelation( HitCount, object_id_field='object_pk',
related_query_name='hit_count_generic_relation' )
That's it. Logout and login again to your app - I guess to refresh the Django sessions framework (I had to).Then, at the template, call the profile views like a regular local ...
<p> Profile Views: {{profile_views}} </p>
I am building an API with Django Rest Framework (DRF) and enabled the authentication/registration through social apps.
For authenticating users via their social accounts I use Django rest-framework Social Oauth2 and it works like a charm. To be sure my user is logged in I created a very simple view in the views.py of my app:
def index(request):
return HttpResponse("is_anonymous: %s" % request.user.is_anonymous)
The result in the browser is the following (it means that the user is logged in):
is_anonymous: False
Now as I am building an API with DRF I may need to retrieve some data of the current user (from request.user) in one of my viewsets but in the following code, the result is not what I expected:
class HelloViewSet(viewsets.ModelViewSet):
queryset = Hello.objects.all()
serializer_class = HelloSerializer
# This is just a random ViewSet, what is
# important is the custom view below
#action(detail=False)
def test(self, request):
return Response(request.user.is_anonymous)
Here the result shows that the user not logged in:
True
So the first view shows that request.user.is_anonymous = False and the second shows that request.user.is_anonymous = True. Both views are in the same file views.py in the same app.
What do I miss here? We are not supposed to get the user instance in an API REST?
I suppose this is because your first view is pure Django and it's not using DRF's DEFAULT_AUTHENTICATION_CLASSES. To enable it, you can add #api_view decorator:
from rest_framework.decorators import api_view
from rest_framework.response import Response
#api_view()
def index(request):
return Response("is_anonymous: %s" % request.user.is_anonymous)
Also you should update DEFAULT_AUTHENTICATION_CLASSES to enable OAuth, like this:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'rest_framework_social_oauth2.authentication.SocialAuthentication',
),
}
As neverwalkaloner mentioned in the in the comments, the problem was that I didn't pass any access_token in the header via Authorization: Bearer <token>, so of course the server wasn't able to identify the "logged" user that was making the request. Using curl (or Postman) I could add this token for checking purpose and it worked.
I want to allow the django users to use a key in the api urls for authentication.
I do not have OAUTH set up yet so I guess the key could be a sesion key or a digest key.
I'm having 2 problems.
I've tried sending this request:
http://192.166.166.11:8000/api?task=search&term=115&csrf_token=s69WAIZqlCTur1XZQr72QhCc7fzqaRtM
First of all, I've tried using the csrf_token but it does not work.
It takes me to the login page.
Secondly, I do not know how to retrieve csrf_token of other users (the admin is trying to get their csrf_tokens).
My attempt:
x = User.objects.get(username='someone')
x.get_session_auth_hash()
gives me the user's authentication hash but it is a different value.
Can someone please guide me get these two problems sorted out?
You are creating a token-based authentication. You already mentioned OAUTH as one option, and I strongly recommend using one of the existing implementations like django-oauth-toolkit. However, you can also create your own quick solution to create a token-based authentication.
Disclaimer: This is for demo purposes only. Do not copy it in any existing project. It will make your application vulnerable.
First, we create an additional model handling the authentication tokens:
/auth_tokens/models.py
from django.db import models
from django.conf import settings
import string, random
def random_string(length = 64, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for x in range(length))
class AuthToken(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
token = models.CharField(max_length=64, default=random_string)
/auth_tokens/middleware.py
from auth_tokens.models import AuthToken
class AuthTokenMiddleware:
def process_request(self, request):
token = request.GET.get('auth', None)
if not token:
return
token = AuthToken.objects.get(token=token)
request.user = token.user
return request
Including the middleware into your settings.MIDDLEWARE_CLASSES should enable you to add ?token=<token> to your URL to login your users.
I ended up using token authentication:
http://www.django-rest-framework.org/api-guide/authentication/
so I'd like to share the workflow.
First, you need to do the set up. In settings.py, modify INSTALLED_APPS and add REST_FRAMEWORK as in documentation.
Then you need to run python manage.py syncdb because it needs to add some tables.
Then, you need to add some urls to urls.py to route the api.
You can create and retrieve tokens using this code:
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=User.objects.get(username='john'))
print token.key
Lastly, you'll have to modify your view which depends on whether you're using a function based or class based view.
Here is a function based view I used:
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.decorators import authentication_classes, permission_classes
from rest_framework.decorators import api_view
#api_view(['GET', 'POST'])
#authentication_classes((TokenAuthentication,))
#permission_classes((IsAuthenticated,))
#login_required
def mybooks(request):
entries = Book.objects.all()
return render(request, 'mybooks.html', {'entries': entries})
Lastly, to test it out:
import requests
token = '243124c52f7583e320d043c4395bd99f63344035'
headers = {'Authorization' : 'Token {}'.format(token)}
page = requests.post('http://127.0.0.1:8000/mybooks/', headers=headers)
print page.content
Note that in my case I do not need define plain serialization since I have an advanced custom serialization and that is not the topic here.
Django doesn't provide API Keys out of the box.
Use API providers such as Tastypie to have this feature
I'm implementing an API using Django Rest framework. I wonder Python can send POST params as an array like Ruby does?
For example:
POST /api/end_point/
params = { 'user_id': [1,2,3] }
# In controller, we get an array of user_id:
user_ids = params[:user_id] # [1,2,3]
There are a number of ways to deal with this in django-rest-framework, depending on what you are actually trying to do.
If you are planning on passing this data through POST data then you should use a Serializer. Using a serializer and django-rest-frameworks Serializer you can provide the POST data through json or through a form.
Serializer documentation: http://www.django-rest-framework.org/api-guide/serializers/
Serializer Field documentation: http://www.django-rest-framework.org/api-guide/fields/
Specifically you will want to look at the ListField.
It's not tested, but you will want something along the lines of:
from rest_framework import serializers
from rest_framework.decorators import api_view
class ItemSerializer(serializers.Serializer):
"""Your Custom Serializer"""
# Gets a list of Integers
user_ids = serializers.ListField(child=serializers.IntegerField())
#api_view(['POST'])
def item_list(request):
item_serializer = ItemSerializer(data=request.data)
item_serializer.is_valid(raise_exception=True)
user_ids = item_serializer.data['user_ids']
# etc ...