So I'm working on a shipping website with the django rest framework. The website brings two to four people together so they can easily ship their goods together at the same time. But I'm facing a major stumbling block on the views where user book a shipping the code is below.
models.py
from django.db import models
from django.contrib import get_user_model
User = get_user_model()
class Container(models.Model):
container_type = models.Charfield(max_length = 30, blank=False, null = False)
max_users = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places =2, default=0, blank=True, null=True)
users = models.ManyToManyField(User)
class Ship(models.Model):
container = models.ForeignKey(Container, related_name='cont', on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name='shipper', on_delete=models.CASCADE)
location = (
('France', 'France'),
)
from_location = models.CharField(max_length=30, choices=location, blank=False, null=False)
to_location = (
('Lagos', 'Lagos'),
('Abuja', 'Abuja'),
('Abeokuta', 'Abeokuta'),
('Osun', 'Osun'),
)
to_location = models.CharField(max_length=30, choices=to_location, blank=False, null=False)
date_leaving = models.DateField(auto_now=False)
price = models.DecimalField(max_digits=10, decimal_places=2, default=0, blank=True, null=True)
def __str__(self):
return self.user
then my serializer.py file
from rest_framework import serializers
from .models import Container, Ship
class ContainerSerializer(serializers.ModelSerializer):
class Meta:
model = Container
fields = '__all__'
class MiniContainerSerializer(serializers.ModelSerializer):
class Meta:
model = Container
fields =['container_type', 'price']
class ShipSerializer(serializers.ModelSerializer):
class Meta:
model = Ship
fields = '__all__'
read_only_fields = ('user', 'price')
class MiniShipSerializer(serializers.ModelSerializer):
class Meta:
model = Ship
fields = ['container', 'from_location', 'to_location']
and now my views.py file which I have issues with
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from rest_framework.generics import ListCreateAPIView, CreateAPIView, ListAPIView, RetrieveUpdateDestroyAPIView, RetrieveAPIView
from .serializers import ContainerSerializer, MiniContainerSerializer, ShipSerializer, MiniShipSerializer
from rest_framework import permissions, status
from rest_framework.response import Response
from .models import Container, Ship
class ShipAPI(ListCreateAPIView):
serializer_class = ShipSerializer
def get_queryset(self):
user = self.request.user
queryset = Ship.objects.filter(user=user)
return queryset
def Book_shipping(self, request, *args, **kwargs):
user = request.user
container = get_object_or_404(Container, pk=request.data['container'])
if container.users.count() >= container.max_users:
return Response('container already full')# here i'm trying to set limits so the users joining each container won't surpass the max users.
cont = container(users=user)
cont.save()
from_location = (request.data['from_location'])
to_location = (request.data['to_location'])
date_leaving = int(request.data['date_leaving'])
price = container.price / container.max_users
cart = Ship(container=container, user=user, from_location=from_location, to_location=to_location, date_leaving=date_leaving, price=price)
cart.save()
serializer = ShipSerializer(cart)
data ={'message': 'shipping successfully created',
'data':serializer.data}
return Response(data=data, status=status.HTTP_201_CREATED)
and then after testing the endpoint it returns this error:
IntegrityError at /Shipping/Ship/
NOT NULL constraint failed: shipping_ship.user_id
I've tried debugging and looking at it over and over again can someone please help me? Thanks in advance. And yes I've tried deleting migrations and the database.
As your Container model have a ManyToMany relationship with the User model.
So it may not work like cont = container(users=user)
For me it worked like this:
cont = container.users.add(user)
cont.save()
Related
I have 2 models in my django app, the first is Tags model and the second is Post model, the problem is when i try to use mutation for the Post model to add a post from the graphql it doesn't work but it works fine in the Tags model, also the Queries works fine when i try to get data from the database.
Here's my Code:
model.py:
from django.db import models
from django.utils.translation import gettext as _
from django.conf import settings
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class Tag(models.Model):
name = models.CharField(_("Tag Name"), max_length=50, unique=True)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(_("Title"), max_length=50, unique=True)
slug = models.SlugField(_("Post Slug"), unique=True)
body = models.TextField(_("Post Content"))
createdAt = models.DateTimeField(auto_now_add=True)
updatedAt = models.DateTimeField(auto_now=True)
published = models.BooleanField(_("Published"), default=False)
author = models.ForeignKey(User, verbose_name=_("Author"), on_delete=models.CASCADE)
tags = models.ManyToManyField("Tag", verbose_name=_("Tags"), blank=True)
class Meta:
ordering = ['-createdAt']
def __str__(self):
return self.title
schema.py:
import graphene
from graphene_django import DjangoObjectType
from django.contrib.auth import get_user_model
from .models import Post, Tag
User = get_user_model()
class PostType(DjangoObjectType):
class Meta:
model = Post
class TagType(DjangoObjectType):
class Meta:
model = Tag
fields = ('id', 'name',)
class TagInput(graphene.InputObjectType):
name = graphene.String(required=True)
class CreatePostMutation(graphene.Mutation):
class Arguments:
title = graphene.String()
body = graphene.String()
published = graphene.Boolean()
author_id = graphene.ID()
tags = graphene.List(graphene.Int)
success = graphene.Boolean()
post = graphene.Field(PostType)
#classmethod
def mutate(cls, root, info, title, body, published, author_id, tags):
author = User.objects.get(pk=author_id)
post_instance = Post(
title=title,
body=body,
published=published,
author=author,
)
post_instance.save(commit=False)
if tags:
tag_objects = Tag.objects.filter(pk__in=tags)
post_instance.tags.set(tag_objects)
post_instance.save()
return CreatePostMutation(success=True, post=post_instance)
class CreateTagMutation(graphene.Mutation):
class Arguments:
name = graphene.String(required=True)
tag = graphene.Field(TagType)
#classmethod
def mutate(cls, root, info, name):
tag = Tag(name=name)
tag.save()
return CreateTagMutation(tag=tag)
class Mutation(graphene.ObjectType):
create_post = CreatePostMutation.Field()
create_tag = CreateTagMutation.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
Here's what it returns:
{
"data": {
"createPost": {
"post": null
}
}
}
Working on a django project I am a bit stuck on data representation through APIs. In fact when designing models the data model is quite stratighforward : I have a one to many relationship A--> B
therefore I have added a FK to object B.
Object B has a boolean attribute "active".
I would like to make an API call to list all A objects having at least one assoicated object B with active = true.
The api could be like this :
/api/objectA/?ObjectB.active=True
Here is my code :
Models :
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
class Startup(models.Model):
header = models.CharField("Header", max_length=255)
title = models.CharField("Title", max_length=255)
description = models.CharField("description", max_length=255)
# TODO Change this to options instead of array
tags = ArrayField(models.CharField(max_length=10, blank=True), size=5)
# TODO Images to be stored in aws only url will be in DB
card_image = models.ImageField(upload_to='media/images/cards')
logo_image = models.ImageField(upload_to='media/images/logos')
main_img = models.ImageField(upload_to='media/images/main', null=True)
createdAt = models.DateTimeField("Created At", auto_now_add=True)
def __str__(self):
return self.title
class Investment(models.Model):
# TODO change the name of Investment to fund round in back and front
# TODO all price to be checked for max digits and decimal places
startup = models.ForeignKey(Startup, related_name='startup_investments', on_delete=models.CASCADE, default="1")
# Use the related_name as a serializer bale for investments inside startup serializer
Investment_title = models.CharField("Investment_title", max_length=255, default="Missing Title")
collected_amount = models.DecimalField(max_digits=12, decimal_places=2)
goal_percentage = models.IntegerField(default=0)
number_of_investors = models.IntegerField(default=0)
days_left = models.IntegerField()
active = models.BooleanField(default=False)
# TODO Need to update this to prevent linking to a non existing startup
createdAt = models.DateTimeField("Created At", auto_now_add=True)
def clean(self):
"""Validate that the startup does not have already an active Investment """
if self.active:
qs = Investment.objects.filter(active=True).filter(startup=self.startup)
if self.pk is not None:
qs = qs.exclude(pk=self.pk)
if qs:
raise ValidationError(message="An active investment already exists for this startup")
def __str__(self):
return self.Investment_title
Serializers :
from rest_framework import serializers
from .models import Startup, Investment
class InvestmentSerializer(serializers.ModelSerializer):
class Meta:
model = Investment
fields = ('id', 'Investment_title', 'collected_amount', 'goal_percentage', 'number_of_investors',
'days_left', 'active')
class StartupSerializer(serializers.ModelSerializer):
startup_investments = InvestmentSerializer(many=True, read_only=True)
class Meta:
model = Startup
fields = ('id', 'header', 'title', 'description', 'tags', 'card_image',
'logo_image', 'main_img', 'startup_investments')
Views :
from django_filters import rest_framework as filters
from rest_framework.viewsets import ModelViewSet
from rest_framework_extensions.mixins import NestedViewSetMixin
from .serializers import *
class StartUpViewSet(NestedViewSetMixin, ModelViewSet):
"""
Class that provides List, Retrieve, Create, Update, Partial Update and Destroy actions for startups.
It also include a filter by startup status
"""
model = Startup
queryset = Startup.objects.all()
serializer_class = StartupSerializer
class InvestmentViewSet(NestedViewSetMixin, ModelViewSet):
"""
Class that provides List, Retrieve, Create, Update, Partial Update and Destroy actions for Investments.
It also include a active and investment title
"""
model = Investment
serializer_class = InvestmentSerializer
queryset = Investment.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ('active', 'Investment_title')
routers :
router = ExtendedSimpleRouter()
(
router.register(r'api/investments', views.InvestmentViewSet, basename='investment'),
router.register(r'api/startups', views.StartUpViewSet, basename='startup')
.register(r'investments', views.InvestmentViewSet, basename='startups_investment',
parents_query_lookups=['startup']),
)
Thanks for your help.
I would try something like this:
class StartUpViewSet(NestedViewSetMixin, ModelViewSet):
model = Startup
#queryset = Startup.objects.all()
serializer_class = StartupSerializer
def get_queryset(self):
Startup.objects.annotate(active_investments=Count('startup_investments', filter=Q(startup_investments__active=True)).filter(active_investments__gt=0)
Hello I am posting this answer hoping it will help others as I have spent two days to make this work!!
class ActiveStartupSerializer(serializers.ListSerializer):
def to_representation(self, data):
"""List all startups with one active investment"""
data = data.filter(startup_investments__active=True)
return super(ActiveStartupSerializer, self).to_representation(data)
class Meta:
model = Startup
fields = ('id', 'header', 'title', 'description', 'tags', 'card_image',
'logo_image', 'main_img', 'startup_investments')
class InvestmentSerializer(serializers.ModelSerializer):
class Meta:
model = Investment
fields = ('id', 'Investment_title', 'collected_amount', 'goal_percentage', 'number_of_investors',
'days_left', 'active')
class StartupSerializer(serializers.ModelSerializer):
startup_investments = InvestmentSerializer(many=True, read_only=True)
class Meta:
model = Startup
list_serializer_class = ActiveStartupSerializer
fields = ('id', 'header', 'title', 'description', 'tags', 'card_image',
'logo_image', 'main_img', 'startup_investments')
The field which is specified in my models file is not included in the GraphiQL, I have tried to rename the field, delete it and define it again, even changing the type of field also updating the graphene-django package. None of these I have mentioned didn't work. The name of the field I can't get is named book
models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from books.models import Book
class Borrowing(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
book = models.OneToOneField(Book, null=True, on_delete=models.CASCADE)
date = models.DateTimeField(default=timezone.now)
returned = models.BooleanField(default=False)
date_borrowed = models.CharField(blank=True, null=True, max_length=50)
date_returned = models.CharField(blank=True, null=True, max_length=50)
class Meta:
ordering = ['-date']
def __str__(self):
return f'{self.user.username} borrowed {self.book.title}'
schema.py
import graphene
from .mutations.borrowings import *
from backend.functions import pagination
PAGE_SIZE = 12
class BorrowingMutation(graphene.ObjectType):
borrow_book = BorrowBook.Field()
return_book = ReturnBook.Field()
class BorrowingQuery(graphene.ObjectType):
borrowings = graphene.List(BorrowingType)
users_borrowings = graphene.List(BorrowingType, page=graphene.Int())
def resolve_borrowings(self, info):
return Borrowing.objects.all()
def resolve_users_borrowings(self, info, page):
user = info.context.user
borrowings = Borrowing.objects.filter(user=user, returned=False)
borrowings = pagination(PAGE_SIZE, page, borrowings)
return borrowings
Type
class BorrowingType(DjangoObjectType):
class Meta:
model = Borrowing
I am making a Django application API, where an authorized user can post a messages, and other users can browse the message and like/dislike/comment on the messages (twitter style).
I have implemented oAuth2 already and tested it.
The API works fine from the admin panel, but when I access it through url, it is giving error:
enter image description here
My models.py class is:
class Posts(models.Model):
def expirationTimeCalculation(self):
EXPIRATION_DURATION = 86400 #time in seconds
expirationTime = self.creationTimestamp + timedelta(seconds = EXPIRATION_DURATION)
return expirationTime
def postLiveStatus(self):
return (self.expirationTimestamp > timezone.now)
def postLiveTimeRemaining(self):
if (self.expirationTimestamp > timezone.now):
return (self.expirationTimestamp - timezone.now).total_seconds()
else:
return 0
postIdentifier = models.IntegerField()
title = models.CharField(max_length=100)
message = models.TextField()
topic = models.ManyToManyField('Topics')
creationTimestamp = models.DateTimeField(default=timezone.now)
expirationTimestamp = property(expirationTimeCalculation)
post_owner = models.ForeignKey(AppUser, related_name='post_owner', on_delete=models.CASCADE)
isLive = property(postLiveStatus)
post_likes = models.ForeignKey('Likes', related_name="post_likes", on_delete=models.CASCADE)
post_dislikes = models.ForeignKey('Dislikes', related_name="post_dislikes", on_delete=models.CASCADE)
post_comments = models.ForeignKey('Comments', related_name="post_comments", on_delete=models.CASCADE)
def __str__(self):
return self.title
class Topics(models.Model):
TOPICS = [('P','Politics'),('H','Health'),('S','Sports'),('T','Tech')]
topicName = models.CharField(max_length=2, choices=TOPICS, blank=False)
class Likes(models.Model):
isLiked = models.BooleanField(null=True)
likes_owner = models.ForeignKey(AppUser, related_name='likes_owner', on_delete=models.CASCADE)
post = models.ForeignKey(Posts, related_name='likes', on_delete = models.CASCADE)
class Dislikes(models.Model):
isDisliked = models.BooleanField(null=True)
dislikes_owner = models.ForeignKey(AppUser, related_name='dislikes_owner', on_delete=models.CASCADE)
post = models.ForeignKey(Posts, related_name='dislikes', on_delete = models.CASCADE)
class Comments(models.Model):
comment = models.TextField(null=True)
comments_owner = models.ForeignKey(AppUser, related_name='comments_owner', on_delete=models.CASCADE)
post = models.ForeignKey(Posts, related_name='comments', on_delete = models.CASCADE)
The serializer class is:
class CommentsSerializer(serializers.ModelSerializer):
comments_owner = CreateUserSerializer()
class Meta:
model = Comments
fields = ('comment', 'comments_owner')
class DislikesSerializer(serializers.ModelSerializer):
dislikes_owner = CreateUserSerializer()
class Meta:
model = Dislikes
fields = ('isDisliked', 'dislikes_owner')
class LikesSerializer(serializers.ModelSerializer):
likes_owner = CreateUserSerializer()
class Meta:
model = Likes
fields = ('isLiked', 'likes_owner')
class TopicsSerializer(serializers.ModelSerializer):
class Meta:
model = Topics
fields = ('topicName')
class PostsSerializer(serializers.ModelSerializer):
class Meta:
model = Posts
fields = ('postIdentifier', 'title', 'message', 'topic', 'creationTimestamp', 'expirationTimestamp', 'isLive', 'post_owner', 'post_likes', 'post_dislikes', 'post_comments')
Views class is:
from rest_framework.generics import ListAPIView
from .models import Posts
from .serializers import PostsSerializer
class PostsView(ListAPIView):
queryset = Posts.objects.all()
serilaizer_class = PostsSerializer
And the main URLS class is:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
import posts_backend.views
urlpatterns = [
path('admin/', admin.site.urls),
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')), #Adding oAuth2 privider URLS to the url path
path('authentication/', include('accounts.urls')), #Creating authentication endpoints http://10.61.64.150:8000/authentication/ for performing authentication
path('v1/posts/', posts_backend.views.PostsView.as_view(),), # Version 1 of the API
]
Can anyone kindly let me know why I am getting this error and how to resolve this?
try renaming the attribute from serilaizer_class to serializer_class
I can't understand why this code doesn't work. I have a model called CustomUser with a BooleanField field called expert. I want to filter the users to include users where expert = True.
I've tried self.expert, user___expert and customuser__expert in place of "expert" and none of these worked.
views.py:
from django.shortcuts import render
from django.views.generic import TemplateView, ListView
from users.models import CustomUser
class BrowseView(ListView):
model = CustomUser
template = 'expert_list.html'
def get_queryset(self):
experts = CustomUser.objects.filter(expert == True)
return experts
models.py:
class CustomUser(AbstractUser):
objects = CustomUserManager()
position = models.CharField(max_length =50, null=True, default='')
bio = models.CharField(max_length=300, null=True, default='')
expert = models.BooleanField(blank=True, default=False)
You can filter by passing a named argument in the .filter(..) call, for example:
class BrowseView(ListView):
model = CustomUser
template = 'expert_list.html'
queryset = CustomUser.objects.filter(expert=True)