Related
I have created an API from Database, I can view the API but I am unable to do a query via URL for example: 127.0.0.1:8000/author?author_id=9, I am not sure where to add the query code. I want to filter using fields. Here is my models.py
class AuthorAPI(models.Model):
author_id=models.IntegerField()
name=models.TextField()
author_img_url=models.TextField()
title=models.TextField()
first_published_at=models.DateTimeField()
excerpt=models.TextField()
class Meta:
db_table = 'view_author'
serializers.py
from rest_framework import serializers
from .models import SortAPI, AuthorAPI
class AuthorAPISerializer(serializers.ModelSerializer):
class Meta:
model=AuthorAPI
fields='__all__'
views.py
from .serializers import APISerializer,AuthorAPISerializer
from .models import SortAPI, AuthorAPI
from rest_framework.response import Response
from rest_framework.decorators import api_view
#api_view(['GET'])
def getauthor(request):
if request.method == 'GET':
results = AuthorAPI.objects.all()
serialize = AuthorAPISerializer(results, many=True)
return Response(serialize.data)
In your views, use a ModelViewset
And add the fliter_backend attribute:
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
See here in the docs:
https://www.django-rest-framework.org/api-guide/filtering/#setting-filter-backends
class AuthorViewset(viewsets.ReadOnlyModelViewset):
serializer_class = AuthorAPISerializer
queryset = AuthorAPI.objects.all()
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
IMPORTANT
Using django_filter will require you to install additional requirements, buts its well worth it, see installation steps for django_filter here:
https://django-filter.readthedocs.io/en/stable/guide/install.html
And in your urls.py you need to register your viewser with a SimpleRouter as described in the docs here:
https://www.django-rest-framework.org/api-guide/viewsets/#example
Additionally, you'll need to set the filterset_fields to tell DRF what fields you want to allow the user to filter with.
As specified in the docs here:
https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html#using-the-filterset-fields-shortcut
And important word of warning which might not be emphasised enough in the documentation is this point:
Note that using filterset_fields and filterset_class together is not supported.
Once complete, if you browse to /author you should see some filter controls available, et voila
You can use request.GET to get data from URL parameters.
Give this a try
#api_view(['GET'])
def getauthor(request):
if request.method == 'GET':
results = AuthorAPI.objects.all()
# get author_id from the url query parameter
author_id = request.GET.get('author_id', None)
#if author_id is present in the url query parameter then filter the resluts queryset based on the author_id
if author_id:
results = results.filter(author_id=author_id)
serialize = AuthorAPISerializer(results, many=True)
return Response(serialize.data)
Thanks to Swift but there was some errors, viewsets.ReadOnlyModelViewset wasn't working perfectly so I tried this
views.py
import django_filters.rest_framework
from django.contrib.auth.models import User
from rest_framework import generics,viewsets,filters
from django_filters.rest_framework import DjangoFilterBackend,OrderingFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.renderers import JSONRenderer
class CustomPagination(PageNumberPagination):
page_size = 50
page_size_query_param = 'page_size'
max_page_size = 1000
class AuthorViewset(generics.ListAPIView):
renderer_classes = [JSONRenderer]
pagination_class = CustomPagination
serializer_class = AuthorAPISerializer
queryset = AuthorAPI.objects.all()
filter_backends = [DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter]
filterset_fields = ['name', 'id','author_id','status','title','first_published_at','story_type']
search_fields=['id','author_id','name','title','first_published_at']
ordering_fields=['id','author_id','name','title','first_published_at']
class Meta:
name="AuthorViewset"
I could have used Sumithran's answer but it was a bit complex if I would like to allow multiple fields because for every field I had to add the same code with some modification which could increase code lines.
Given a Django model with a JSONField, what is the correct way of serializing and deserializing it using Django Rest Framework?
I've already tried crating a custom serializers.WritableField and overriding to_native and from_native:
from json_field.fields import JSONEncoder, JSONDecoder
from rest_framework import serializers
class JSONFieldSerializer(serializers.WritableField):
def to_native(self, obj):
return json.dumps(obj, cls = JSONEncoder)
def from_native(self, data):
return json.loads(data, cls = JSONDecoder)
But when I try to updating the model using partial=True, all the floats in the JSONField objects become strings.
If you're using Django Rest Framework >= 3.3, then the JSONField serializer is now included. This is now the correct way.
If you're using Django Rest Framework < 3.0, then see gzerone's answer.
If you're using DRF 3.0 - 3.2 AND you can't upgrade AND you don't need to serialize binary data, then follow these instructions.
First declare a field class:
from rest_framework import serializers
class JSONSerializerField(serializers.Field):
""" Serializer for JSONField -- required to make field writable"""
def to_internal_value(self, data):
return data
def to_representation(self, value):
return value
And then add in the field into the model like
class MySerializer(serializers.ModelSerializer):
json_data = JSONSerializerField()
And, if you do need to serialize binary data, you can always the copy official release code
In 2.4.x:
from rest_framework import serializers # get from https://gist.github.com/rouge8/5445149
class WritableJSONField(serializers.WritableField):
def to_native(self, obj):
return obj
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
my_json_field = WritableJSONField() # you need this.
If and only if you know the first-level style of your JSON content (List or Dict), you can use DRF builtin DictField or ListField.
Ex:
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
my_json_field = serializers.DictField()
It works fine, with GET/PUT/PATCH/POST, including with nested contents.
serializers.WritableField is deprecated. This works:
from rest_framework import serializers
from website.models import Picture
class PictureSerializer(serializers.HyperlinkedModelSerializer):
json = serializers.SerializerMethodField('clean_json')
class Meta:
model = Picture
fields = ('id', 'json')
def clean_json(self, obj):
return obj.json
Mark Chackerian script didn't work for me, I'd to force the json transform:
import json
class JSONSerializerField(serializers.Field):
""" Serializer for JSONField -- required to make field writable"""
def to_internal_value(self, data):
json_data = {}
try:
json_data = json.loads(data)
except ValueError, e:
pass
finally:
return json_data
def to_representation(self, value):
return value
Works fine. Using DRF 3.15 and JSONFields in Django 1.8
For the record, this "just works" now if you are using PostgreSQL, and your model field is adjango.contrib.postgres.JSONField.
I'm on PostgreSQL 9.4, Django 1.9, and Django REST Framework 3.3.2.
I have previously used several of the other solutions listed here, but was able to delete that extra code.
Example Model:
class Account(models.Model):
id = UUIDField(primary_key=True, default=uuid_nodash)
data = JSONField(blank=True, default="")
Example Serializer:
class AccountSerializer(BaseSerializer):
id = serializers.CharField()
class Meta:
model = Account
fields = ('id','data')
Example View:
class AccountViewSet(
viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin
):
model = Account
queryset = Account.objects.all()
serializer_class = AccountSerializer
filter_fields = ['id', 'data']
Thanks by the help. This is the code i finally use for render it
class JSONSerializerField(serializers.Field):
"""Serializer for JSONField -- required to make field writable"""
def to_representation(self, value):
json_data = {}
try:
json_data = json.loads(value)
except ValueError as e:
raise e
finally:
return json_data
def to_internal_value(self, data):
return json.dumps(data)
class AnyModelSerializer(serializers.ModelSerializer):
field = JSONSerializerField()
class Meta:
model = SomeModel
fields = ('field',)
If you're using mysql (haven't tried with other databases), using both DRF's new JSONField and Mark Chackerian's suggested JSONSerializerField will save the json as a {u'foo': u'bar'} string.
If you rather save it as {"foo": "bar"}, this works for me:
import json
class JSONField(serializers.Field):
def to_representation(self, obj):
return json.loads(obj)
def to_internal_value(self, data):
return json.dumps(data)
DRF gives us inbuilt field 'JSONField' for binary data, but JSON
payload is verified only when you set 'binary' flag True then it convert into utf-8 and load the JSON payload, else it only
treat them as string(if invalid json is sent) or json and validate both
without error even though you cretaed JSONField
class JSONSerializer(serializers.ModelSerializer):
"""
serializer for JSON
"""
payload = serializers.JSONField(binary=True)
To serialize a data from a request you can use the serializers.ModelSerializer
serializers.py
from rest_framwork import serializers
class FinalSerializer(serializers.ModelSerializer):
class Meta:
model=Student
fields='__all__'
views.py
import io
from yourappname.serializers import FinalSerializer #replace your app name
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser,MultiPartParser,FormParser
from rest_framework.response import Response
class DataList(APIView):
parser_classes = (JSONParser,MultiPartParser,FormParser) #If you are using postman
renderer_classes = (JSONRenderer,)
#Serialize
def get(self,request,format=None):
all_data=Student.objects.all()
serializer=FinalSerializer(all_data,many=True)
return Response(serializer.data)#Will return serialized json data,makes sure you have data in your model
#Deserialize
#Not tried this function but it will work
#from django documentation
def djson(self,request,format=None):
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
serializer = FinalSerializer(data=data)
serializer.is_valid()
serializer.validated_data
If you want JSONField for mysql this is done in django-mysql and serializer was fixed some day ago [1], is not yet in any release.
[1] https://github.com/adamchainz/django-mysql/issues/353
setting.py
add:
'django_mysql',
models.py
from django_mysql.models import JSONField
class Something(models.Model):
(...)
parameters = JSONField()
I'm trying to develop a website for an online store, and after creating and registering models, I don't know why, this error is thrown. What can I do? And also after running the migrate command, it is saying no migrations to apply. How can I do this?
My models.py:
from django.db import models
import stripe
from django.conf import settings
from django.contrib.auth.signals import user_logged_in
from django.contrib.auth.models import User
stripe.api_key = settings.STRIPE_SECRET_KEY
class UserStripe(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
stripe_id = models.CharField(max_length=120)
def __unicode__(self):
return str(self.stripe_id)
def get_or_create_stripe(sender, user, *args, **kwargs):
try:
user.userstripe.stripe_id
except UserStripe.DoesNotExist:
customer = stripe.Customer.create(
email = str(user.email)
)
new_user_stripe = UserStripe.objects.create(
user = user,
stripe_id = customer.id
)
except:
pass
user_logged_in.connect(get_or_create_stripe)
Have you changed anything since your last migration ?
I think you might have changed some models, now django is unable to find your table.
I would delete the current migration files and create new ones.
Django and MongoDB
Supporting a different set of fields for each document in a collection is one of MongoDB's features. It allows you to store similar data, but with different properties in the same collection.
for example:
{
_id: ObjectId("51156a1e056d6f966f268f81"),
type: "Article",
author: "Derick Rethans",
title: "Introduction to Document Databases with MongoDB",
date: ISODate("2013-04-24T16:26:31.911Z"),
body: "This artiā¦"
},
{
_id: ObjectId("51156a1e056d6f966f268f82"),
type: "Book",
author: "Derick Rethans",
title: "php|architect's Guide to Date and Time Programming with PHP",
isbn: "978-0-9738621-5-7"
}
Django dose not support Non-Relational data base like mongodb by default, but there are some lib's for this purpose. for example Django MongoDB Engine is a MongoDB backend for Django.
MongoDB allow to use different set of fields for each document in a collection, but in django you have to define models.py:
from django.db import models
from djangotoolbox.fields import ListField
class Post(models.Model):
title = models.CharField()
text = models.TextField()
tags = ListField()
comments = ListField()
the Question is: is there any way to define different set of fields for each document in a collection in MongoDB, when using Django ?
The Alternative
I like using django-mongoengine as it makes things clearer when dealing with MongoDB models.
For example, you can create structured Documents that are going to be transformed into models or EmbeddedDocument`s that are structured documents to be used in an already existed model.
from django_mongoengine import Document, EmbeddedDocument, fields
class Comment(EmbeddedDocument):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
author = fields.StringField(verbose_name="Name", max_length=255)
email = fields.EmailField(verbose_name="Email")
body = fields.StringField(verbose_name="Comment")
class Post(Document):
created_at = fields.DateTimeField(
default=datetime.datetime.now, editable=False,
)
title = fields.StringField(max_length=255)
slug = fields.StringField(max_length=255, primary_key=True)
comments = fields.ListField(
fields.EmbeddedDocumentField('Comment'), blank=True,
)
The Answer
So for your case what you need to use is Dynamic document schemas that work in the same way as Document but any data/attributes set to them will also be saved.
class Page(DynamicDocument):
title = StringField(max_length=200, required=True)
# Create a new page and add tags
>>> page = Page(title='Using MongoEngine')
>>> page.tags = ['mongodb', 'mongoengine']
>>> page.save()
>>> Page.objects(tags='mongoengine').count()
>>> 1
I struggled this problem in user profile page. This is my solution.
model.py
from django.contrib.auth.models import User
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,related_name="user_profile")
fullname = models.CharField(max_length=100,verbose_name="full name")
about = models.CharField(max_length=300,blank=True,null=True)
hobies = models.CharField(max_length=200,blank=True)
recent_aktivity = models.CharField(max_length=150,verbose_name="recent activity",null=True)
photo = models.ImageField(blank=True,null=True,upload_to="images/")
recent_badges = models.CharField(max_length=100,verbose_name="recent badges",null=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True,null=True,verbose_name="phone number")
website_url = models.URLField(blank=True,null=True,verbose_name="company website")
projects =models.CharField(max_length=200,blank=True,null=True)
bio = models.CharField(max_length=300,blank=True,null=True)
def __str__(self):
return f'{self.user.username}-ProfileModel'
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
#receiver(post_save,sender=User)
def update_user_profile(sender,instance,created,**kwargs):
if created:
profile = Profile.objects.create(user =instance)
app.py
from django.apps import AppConfig
class ProfileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Profile'
def ready(self):
import Profile.signals
forms.py
from django import forms
from.models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = '__all__'
exclude = ['user']
views.py
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.urls.base import reverse
from .forms import ProfileForm
from .models import Profile
from django.shortcuts import redirect, render,get_object_or_404
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
login_required(login_url="user:login")
def dashboard(request):
return render(request,"dashboard.html")
#login_required(login_url="user:login")
def get_profile(request):
profile = get_object_or_404(Profile,user=request.user)
return render(request,"profile.html",{"profile":profile})
I want to ask about the use of ._meta in this code ? I didn't find a documentation that explains the use of .meta
def resend_activation_email(self, request, queryset):
"""
Re-sends activation emails for the selected users.
Note that this will *only* send activation emails for users
who are eligible to activate; emails will not be sent to users
whose activation keys have expired or who have already
activated.
"""
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
for profile in queryset:
if not profile.activation_key_expired():
profile.send_activation_email(site)
resend_activation_email.short_description = _("Re-send activation emails")
_meta as the name implies stores the meta data of the model. Say, you want to loop over all the fields of a model, then you can use model._meta.get_fields().
By the way, meta options are covered in the documentation -
Model Meta options documentation
You can also use ._meta in testing.
Consider the below models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
published = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.author} {self.title} {self.content}"
You could use ._meta to verify label names of a post (in test_models.py) like:
from datetime import datetime
from django.test import TestCase
from django.contrib.auth.models import User
from blog.models import Post
class TestBlogModels(TestCase):
def setUp(self):
author = User.objects.create(username='TestUser1')
self.post = Post.objects.create(
author=author,
title='Test Post Title 1',
content='Test content for title 1'
)
....
def test_blog_author_label(self):
author_field = Post._meta.get_field('author').verbose_name
self.assertEqual(author_field, "author")
def test_blog_title_label(self):
title_field = Post._meta.get_field('title').verbose_name
self.assertEqual(title_field, "title")
def test_blog_content_label(self):
content_field = Post._meta.get_field('content').verbose_name
self.assertEqual(content_field, "content")
def test_blog_title_max_length(self):
title_field_length = Post._meta.get_field('title').max_length
self.assertEqual(title_field_length, 100)
...
(verbose_name strips django.db.models.fields to the field speicified in models.py)