Django Rest Framework React Project

Hello,

I’ve made a django rest framework api and I’m building a content sharing site that’s using react. I’ve added the frontend and backend into the one repo on github. My API is working for some of the models I’ve made but when it’s comes to creating a community, I’m getting a status 400 error. Also, when I try to delete a post, I get a status 405 error.

community views.py

from rest_framework import generics, filters
from .models import Community
from .serializers import CommunitySerializer
from chatterbox_api.permissions import IsOwnerOrReadOnly
from django.db.models import Count


class CommunityList(generics.ListCreateAPIView):
    queryset = Community.objects.annotate(
        profiles_count=Count('owner__profile', distinct=True),
        posts_count=Count('owner__post', distinct=True)
    ).order_by('-created_at')
    serializer_class = CommunitySerializer
    filter_backends = [
        filters.OrderingFilter
    ]
    ordering_fields = [
        'profiles_count',
        'posts_count',
    ]


class CommunityDetail(generics.RetrieveUpdateAPIView):
    serializer_class = CommunitySerializer
    permission_classes = [IsOwnerOrReadOnly]
    queryset = Community.objects.all()

community serializers.py:

from rest_framework import serializers
from .models import Community


class CommunitySerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    is_owner = serializers.SerializerMethodField()
    posts_count = serializers.ReadOnlyField()
    profiles_count = serializers.ReadOnlyField()

    def get_is_owner(self, obj):
        request = self.context['request']
        return request.user == obj.owner

    class Meta:
        model = Community
        fields = ['id', 'owner', 'is_owner', 'name', 'description', 'users', 'comment',
            'like', 'created_at', 'updated_at', 'posts_count', 'profiles_count']

community models.py:

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from comments.models import Comment
from likes.models import Like


class Community(models.Model):
    owner = models.OneToOneField(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=150)
    description = models.TextField(null=True, blank=True)
    users = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name='users', null=True)
    comment = models.ForeignKey(Comment, on_delete=models.CASCADE, null=True)
    like = models.ForeignKey(Like, on_delete=models.CASCADE, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.name} community"


def create_community(sender, instance, created, **kwargs):
    if created:
        Community.objects.create(owner=instance)


post_save.connect(create_community, sender=User)

React file for community:

import React, { useState } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Container from "react-bootstrap/Container";
import Alert from "react-bootstrap/Alert";
import styles from "../../styles/CreateCommunity.module.css";
import appStyles from "../../App.module.css";
import { useHistory } from "react-router";
import { axiosReq } from '../../api/axiosDefault';
import { useRedirect } from '../../hooks/useRedirect';

function CreateCommunity() {
    useRedirect('loggedOut');
    const [errors, setErrors] = useState({});

    const [community, setCommunity] = useState({
        name: "",
        description: "",
    });
    const { name, description } = community;
    const [chars, setChars] = useState(21);
    const history = useHistory();

    const handleChange = (e) => {
        setCommunity({
            ...community,
            [e.target.name]: e.target.value
        });
    };

    const handleNameLimit = (e) => {
        if (e.target.value.length > 21) return;
        setCommunity(e.target.value);
        setChars(21 - e.target.value.length);
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const { data } = await axiosReq.post("/community/", community);
            history.push(`/community/${data.id}`);
        } catch (err) {
            console.log(err);
            if (err.response?.status !== 401) {
                setErrors(err.response?.data);
            }
        }
    };

    const communityFields = (
        <div className="text-center">
            <div>
                <Form.Group>
                    <Form.Label>Name</Form.Label>
                    <Form.Control
                        type="text"
                        name="name"
                        value={name}
                        onChange={handleNameLimit}
                    />
                    <Form.Label
                        className={styles.NameLimit}
                    >
                        {chars} characters remaining
                    </Form.Label>
                </Form.Group>
                {errors?.name?.map((message, idx) => (
                    <Alert variant="warning" key={idx}>
                        {message}
                    </Alert>
                ))}
            </div>

            <Form.Group>
                <Form.Label>Description</Form.Label>
                <Form.Control
                    as="textarea"
                    rows={6}
                    name="description"
                    value={description}
                    onChange={handleChange}
                />
            </Form.Group>
            {errors?.description?.map((message, idx) => (
                <Alert variant="warning" key={idx}>
                    {message}
                </Alert>
            ))}

            <Button
                onClick={() => history.goBack()}
                className={styles.BtnCancel}
            >
                Cancel
            </Button>
            <Button
                type="submit"
                className={styles.BtnCreate}
            >
                Create
            </Button>
        </div >
    );

    return (
        <Form onSubmit={handleSubmit}>
            <Row>
                <Col lg={4}></Col>
                <Col md={5} lg={4} className="d-none d-md-block p-0 p-md-2">
                    <Container className={appStyles.Content}>
                        <div>
                            {communityFields}
                        </div>
                    </Container>
                </Col>
                <Col lg={4}></Col>
            </Row>
        </Form>
    );
}

export default CreateCommunity;

Any help would be greatly appreciated

Hi FintanMi,

Your Community model needs an owner data. You’re neither sending the owner id form your React app nor handling this condition in your CommunityList view. If you intend to set the request user as the owner of the Community object, you can override your CommunityList view(which can be tricky). In my opinion, in a REST api app, in most cases, it should be frontend’s responsibility to send required data rather than handling the missing data in the backend.

And for the post deletion 405 error, it seems your view is not accepting ‘DELETE’ requests. You may be inheriting from a generic view which does not have a delete method.

class PostDetail(generics.RetrieveUpdateAPIView):
    # This view can not be used for delete requests unless you add a delete method.

class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    # This view can handle delete requests.

The 405 error is gone now so thank you for that! Regarding the owner data for the community model, does this line:

owner = models.ForeignKey(User, on_delete=models.CASCADE)

not mean an id will be generated for that user?

This line means every Community instance has an owner which is an instance of User model. Unless you have null=True in your field arguments, you’ll need to provide an owner id.

1 Like