Omegacrafter
Omegacrafter

Reputation: 23

how can I access Django rest framework using ajax

I am trying to get a form from the rest framework using ajax I already tried the ajax get method on other thing and it worked for me now I am trying to use the POST method to grab the form but i am facing difficulties my current HTML code:

{% extends 'base.html' %}
{% block content %}
<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta name="robots" content="NONE,NOARCHIVE" />
  <title>Create a Recipe</title>
</head>

<body class="">
</body>
</html>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script>
  var data = {title:'title',tags:'tags',ingredients:'ingredients',time_minutes:'time_minutes'}

  $.ajax({
       type: 'POST',
       data: data,
       url: '/api/recipe/recipes/',
       success: function(res){
         for (const i in res) {

               console.log(res[i].)
             }
       },
       error: function(error) {
           callbackErr(error,self)
       }
   })
</script>
{%endblock%}

this is my current attempt on ajax form

serializers.py

class RecipeSerializer(serializers.ModelSerializer):
    """Serialize a recipe"""
    ingredients = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Ingredient.objects.all()
    )
    tags = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Tag.objects.all()
    )

    class Meta:
        model = Recipe
        fields = (
            'id', 'title', 'ingredients', 'tags', 'time_minutes',
            'price', 'link'
        )
        read_only_fields = ('id',)


class RecipeDetailSerializer(RecipeSerializer):
    """Serialize a recipe detail"""
    ingredients = IngredientSerializer(many=True, read_only=True)
    tags = TagSerializer(many=True, read_only=True)


class RecipeImageSerializer(serializers.ModelSerializer):
    """Serializer for uploading images to recipes"""

    class Meta:
        model = Recipe
        fields = ('id', 'image')
        read_only_fields = ('id',)

Models.py

def recipe_image_file_path(instance, filename):
    """Generate file path for new recipe image"""
    ext = filename.split('.')[-1]
    filename = f'{uuid.uuid4()}.{ext}'

    return os.path.join('uploads/recipe/', filename)
class Recipe(models.Model):
    """Recipe object"""
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    title = models.CharField(max_length=255)
    time_minutes = models.IntegerField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    link = models.CharField(max_length=255, blank=True)
    ingredients = models.ManyToManyField('Ingredient')
    tags = models.ManyToManyField('Tag')
    image = models.ImageField(null=True, upload_to=recipe_image_file_path)

    def __str__(self):
        return self.title

i am getting this error in the Network

    RuntimeError at /api/recipe/recipes
You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to 127.0.0.1:8000/api/recipe/recipes/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.

Request Method: POST
Request URL: http://127.0.0.1:8000/api/recipe/recipes
Django Version: 2.1.15
Python Executable: /usr/local/bin/python
Python Version: 3.7.9
Python Path: ['/app', '/usr/local/lib/python37.zip', '/usr/local/lib/python3.7', '/usr/local/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/site-packages']
Server time: Thu, 24 Dec 2020 17:22:57 +0000
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework',
 'rest_framework.authtoken',
 'core',
 'user',
 'recipe',
 'home']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Traceback:

File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py" in __call__
  90.             response = self.process_request(request)

File "/usr/local/lib/python3.7/site-packages/django/middleware/common.py" in process_request
  53.             path = self.get_full_path_with_slash(request)

File "/usr/local/lib/python3.7/site-packages/django/middleware/common.py" in get_full_path_with_slash
  93.                     'url': request.get_host() + new_path,

Exception Type: RuntimeError at /api/recipe/recipes
Exception Value: You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to 127.0.0.1:8000/api/recipe/recipes/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.
Request information:
USER: [unable to retrieve the current user]

GET: No GET data

POST:
title = 'title'
tags = 'tags'
ingredients = 'ingredients'
time_minutes = 'time_minutes'

FILES: No FILES data

COOKIES:
csrftoken = 'e0Cg1uWEHD11XPw6j6euahPGG2pDusWFjc4uYKJF1SIWef9LAAtes6DWYiKFYPWL'
sessionid = 'ry8b8787i2739gkrrk5wy9u29jiqksza'
tabstyle = 'html-tab'

META:
CONTENT_LENGTH = '71'
CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=UTF-8'
DB_HOST = 'db'
DB_NAME = 'app'
DB_PASS = 'daghestani123'
DB_USER = 'postgres'
DJANGO_SETTINGS_MODULE = 'app.settings'
GATEWAY_INTERFACE = 'CGI/1.1'
GPG_KEY = '0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D'
HOME = '/home/user'
HOSTNAME = 'f077452f1043'
HTTP_ACCEPT = '*/*'
HTTP_ACCEPT_ENCODING = 'gzip, deflate, br'
HTTP_ACCEPT_LANGUAGE = 'en-US,en;q=0.9,ar-EG;q=0.8,ar;q=0.7,tr-TR;q=0.6,tr;q=0.5'
HTTP_AUTHORIZATION = 'Token a443195e71b765da5a605013077c4ea991678fb4'
HTTP_CONNECTION = 'keep-alive'
HTTP_COOKIE = 'csrftoken=e0Cg1uWEHD11XPw6j6euahPGG2pDusWFjc4uYKJF1SIWef9LAAtes6DWYiKFYPWL; sessionid=ry8b8787i2739gkrrk5wy9u29jiqksza; tabstyle=html-tab'
HTTP_HOST = '127.0.0.1:8000'
HTTP_ORIGIN = 'http://127.0.0.1:8000'
HTTP_REFERER = 'http://127.0.0.1:8000/create/'
HTTP_SEC_FETCH_DEST = 'empty'
HTTP_SEC_FETCH_MODE = 'cors'
HTTP_SEC_FETCH_SITE = 'same-origin'
HTTP_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
HTTP_X_REQUESTED_WITH = 'XMLHttpRequest'
LANG = 'C.UTF-8'
PATH = '/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
PATH_INFO = '/api/recipe/recipes'
PWD = '/app'
PYTHONUNBUFFERED = '1'
PYTHON_GET_PIP_SHA256 = 'd48ae68f297cac54db17e4107b800faae0e5210131f9f386c30c0166bf8d81b7'
PYTHON_GET_PIP_URL = 'https://github.com/pypa/get-pip/raw/91630a4867b1f93ba0a12aa81d0ec4ecc1e7eeb9/get-pip.py'
PYTHON_PIP_VERSION = '20.3.1'
PYTHON_VERSION = '3.7.9'
QUERY_STRING = ''
REMOTE_ADDR = '172.18.0.1'
REMOTE_HOST = ''
REQUEST_METHOD = 'POST'
RUN_MAIN = 'true'
SCRIPT_NAME = ''
SERVER_NAME = 'f077452f1043'
SERVER_PORT = '8000'
SERVER_PROTOCOL = 'HTTP/1.1'
SERVER_SOFTWARE = 'WSGIServer/0.2'
SHLVL = '1'
TZ = 'UTC'
wsgi.errors = <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
wsgi.file_wrapper = ''
wsgi.input = <django.core.handlers.wsgi.LimitedStream object at 0x7f2c38338a90>
wsgi.multiprocess = False
wsgi.multithread = True
wsgi.run_once = False
wsgi.url_scheme = 'http'
wsgi.version = '(1, 0)'

Settings:
Using settings module app.settings
ABSOLUTE_URL_OVERRIDES = {}
ADMINS = []
ALLOWED_HOSTS = []
APPEND_SLASH = True
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
AUTH_PASSWORD_VALIDATORS = '********************'
AUTH_USER_MODEL = 'core.User'
BASE_DIR = '/app'
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
CACHE_MIDDLEWARE_ALIAS = 'default'
CACHE_MIDDLEWARE_KEY_PREFIX = '********************'
CACHE_MIDDLEWARE_SECONDS = 600
CSRF_COOKIE_AGE = 31449600
CSRF_COOKIE_DOMAIN = None
CSRF_COOKIE_HTTPONLY = False
CSRF_COOKIE_NAME = 'csrftoken'
CSRF_COOKIE_PATH = '/'
CSRF_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SECURE = False
CSRF_FAILURE_VIEW = 'django.views.csrf.csrf_failure'
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
CSRF_TRUSTED_ORIGINS = []
CSRF_USE_SESSIONS = False
DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql', 'HOST': 'db', 'NAME': 'app', 'USER': 'postgres', 'PASSWORD': '********************', 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None, 'PORT': '', 'TEST': {'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}}}
DATABASE_ROUTERS = []
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000
DATETIME_FORMAT = 'N j, Y, P'
DATETIME_INPUT_FORMATS = ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f', '%Y-%m-%d %H:%M', '%Y-%m-%d', '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M:%S.%f', '%m/%d/%Y %H:%M', '%m/%d/%Y', '%m/%d/%y %H:%M:%S', '%m/%d/%y %H:%M:%S.%f', '%m/%d/%y %H:%M', '%m/%d/%y']
DATE_FORMAT = 'N j, Y'
DATE_INPUT_FORMATS = ['%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', '%b %d %Y', '%b %d, %Y', '%d %b %Y', '%d %b, %Y', '%B %d %Y', '%B %d, %Y', '%d %B %Y', '%d %B, %Y']
DEBUG = True
DEBUG_PROPAGATE_EXCEPTIONS = False
DECIMAL_SEPARATOR = '.'
DEFAULT_CHARSET = 'utf-8'
DEFAULT_CONTENT_TYPE = 'text/html'
DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFilter'
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
DEFAULT_FROM_EMAIL = 'webmaster@localhost'
DEFAULT_INDEX_TABLESPACE = ''
DEFAULT_TABLESPACE = ''
DISALLOWED_USER_AGENTS = []
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'localhost'
EMAIL_HOST_PASSWORD = '********************'
EMAIL_HOST_USER = ''
EMAIL_PORT = 25
EMAIL_SSL_CERTFILE = None
EMAIL_SSL_KEYFILE = '********************'
EMAIL_SUBJECT_PREFIX = '[Django] '
EMAIL_TIMEOUT = None
EMAIL_USE_LOCALTIME = False
EMAIL_USE_SSL = False
EMAIL_USE_TLS = False
FILE_CHARSET = 'utf-8'
FILE_UPLOAD_DIRECTORY_PERMISSIONS = None
FILE_UPLOAD_HANDLERS = ['django.core.files.uploadhandler.MemoryFileUploadHandler', 'django.core.files.uploadhandler.TemporaryFileUploadHandler']
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440
FILE_UPLOAD_PERMISSIONS = None
FILE_UPLOAD_TEMP_DIR = None
FIRST_DAY_OF_WEEK = 0
FIXTURE_DIRS = []
FORCE_SCRIPT_NAME = None
FORMAT_MODULE_PATH = None
FORM_RENDERER = 'django.forms.renderers.DjangoTemplates'
IGNORABLE_404_URLS = []
INSTALLED_APPS = ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework.authtoken', 'core', 'user', 'recipe', 'home']
INTERNAL_IPS = []
LANGUAGES = [('af', 'Afrikaans'), ('ar', 'Arabic'), ('ast', 'Asturian'), ('az', 'Azerbaijani'), ('bg', 'Bulgarian'), ('be', 'Belarusian'), ('bn', 'Bengali'), ('br', 'Breton'), ('bs', 'Bosnian'), ('ca', 'Catalan'), ('cs', 'Czech'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('dsb', 'Lower Sorbian'), ('el', 'Greek'), ('en', 'English'), ('en-au', 'Australian English'), ('en-gb', 'British English'), ('eo', 'Esperanto'), ('es', 'Spanish'), ('es-ar', 'Argentinian Spanish'), ('es-co', 'Colombian Spanish'), ('es-mx', 'Mexican Spanish'), ('es-ni', 'Nicaraguan Spanish'), ('es-ve', 'Venezuelan Spanish'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('fi', 'Finnish'), ('fr', 'French'), ('fy', 'Frisian'), ('ga', 'Irish'), ('gd', 'Scottish Gaelic'), ('gl', 'Galician'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('hr', 'Croatian'), ('hsb', 'Upper Sorbian'), ('hu', 'Hungarian'), ('ia', 'Interlingua'), ('id', 'Indonesian'), ('io', 'Ido'), ('is', 'Icelandic'), ('it', 'Italian'), ('ja', 'Japanese'), ('ka', 'Georgian'), ('kab', 'Kabyle'), ('kk', 'Kazakh'), ('km', 'Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('lb', 'Luxembourgish'), ('lt', 'Lithuanian'), ('lv', 'Latvian'), ('mk', 'Macedonian'), ('ml', 'Malayalam'), ('mn', 'Mongolian'), ('mr', 'Marathi'), ('my', 'Burmese'), ('nb', 'Norwegian Bokmål'), ('ne', 'Nepali'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('os', 'Ossetic'), ('pa', 'Punjabi'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('pt-br', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('sq', 'Albanian'), ('sr', 'Serbian'), ('sr-latn', 'Serbian Latin'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('ta', 'Tamil'), ('te', 'Telugu'), ('th', 'Thai'), ('tr', 'Turkish'), ('tt', 'Tatar'), ('udm', 'Udmurt'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese'), ('zh-hant', 'Traditional Chinese')]
LANGUAGES_BIDI = ['he', 'ar', 'fa', 'ur']
LANGUAGE_CODE = 'en-us'
LANGUAGE_COOKIE_AGE = None
LANGUAGE_COOKIE_DOMAIN = None
LANGUAGE_COOKIE_NAME = 'django_language'
LANGUAGE_COOKIE_PATH = '/'
LOCALE_PATHS = []
LOGGING = {}
LOGGING_CONFIG = 'logging.config.dictConfig'
LOGIN_REDIRECT_URL = '/accounts/profile/'
LOGIN_URL = '/accounts/login/'
LOGOUT_REDIRECT_URL = None
MANAGERS = []
MEDIA_ROOT = '/vol/web/media'
MEDIA_URL = '/media/'
MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware']
MIGRATION_MODULES = {}
MONTH_DAY_FORMAT = 'F j'
NUMBER_GROUPING = 0
PASSWORD_HASHERS = '********************'
PASSWORD_RESET_TIMEOUT_DAYS = '********************'
PREPEND_WWW = False
REST_FRAMEWORK = {'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer']}
ROOT_URLCONF = 'app.urls'
SECRET_KEY = '********************'
SECURE_BROWSER_XSS_FILTER = False
SECURE_CONTENT_TYPE_NOSNIFF = False
SECURE_HSTS_INCLUDE_SUBDOMAINS = False
SECURE_HSTS_PRELOAD = False
SECURE_HSTS_SECONDS = 0
SECURE_PROXY_SSL_HEADER = None
SECURE_REDIRECT_EXEMPT = []
SECURE_SSL_HOST = None
SECURE_SSL_REDIRECT = False
SERVER_EMAIL = 'root@localhost'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 1209600
SESSION_COOKIE_DOMAIN = None
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_NAME = 'sessionid'
SESSION_COOKIE_PATH = '/'
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SECURE = False
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_FILE_PATH = None
SESSION_SAVE_EVERY_REQUEST = False
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
SETTINGS_MODULE = 'app.settings'
SHORT_DATETIME_FORMAT = 'm/d/Y P'
SHORT_DATE_FORMAT = 'm/d/Y'
SIGNING_BACKEND = 'django.core.signing.TimestampSigner'
SILENCED_SYSTEM_CHECKS = []
STATICFILES_DIRS = []
STATICFILES_FINDERS = ['django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder']
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
STATIC_ROOT = '/vol/web/static'
STATIC_URL = '/static/'
TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': ['app/templates'], 'APP_DIRS': True, 'OPTIONS': {'context_processors': ['django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages']}}]
TEST_NON_SERIALIZED_APPS = []
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
THOUSAND_SEPARATOR = ','
TIME_FORMAT = 'P'
TIME_INPUT_FORMATS = ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_THOUSAND_SEPARATOR = False
USE_TZ = True
USE_X_FORWARDED_HOST = False
USE_X_FORWARDED_PORT = False
WSGI_APPLICATION = 'app.wsgi.application'
X_FRAME_OPTIONS = 'SAMEORIGIN'
YEAR_MONTH_FORMAT = 'F Y'


You're seeing this error because you have DEBUG = True in your
Django settings file. Change that to False, and Django will
display a standard page generated by the handler for this status code.

pls help me to create a full form thank you oh yeah and I am still a beginner thank you

Upvotes: 0

Views: 2233

Answers (1)

GTBebbo
GTBebbo

Reputation: 1218

The data sent in your ajax request cannot be understood by Django rest.

Response from Django:

{
    ingredients: ["Incorrect type. Expected pk value, received str."],
    price: ["This field is required."],
    tags: ["Incorrect type. Expected pk value, received str."],
    time_minutes: ["A valid integer is required."]
}

We can solve this by altering the data we send:

  • We use a list of numbers (primary keys/ids) for both tags and ingredients, we can use an empty list for now.
  • The time_minutes is no longer a string and is now a number
  • The price field was missing and has also been added as a number
var data = {
    title: 'title',
    tags: [],
    ingredients: [],
    time_minutes: 25,
    price: 10.5
};

This will depend on your Ingredient and Tag serializers, you can even change the data required by specifying your own POST view in the RecipeViewSet. Read more about that in the DRF docs

You will also need to add a user field to the request data and the RecipeSerializer, as our recipes need a user to associate the recipe with:

from django.contrib.auth import get_user_model

# ...

class RecipeSerializer(serializers.ModelSerializer):
    """Serialize a recipe"""
    ingredients = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Ingredient.objects.all()
    )
    tags = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Tag.objects.all()
    )
    user = serializers.PrimaryKeyRelatedField(
        queryset=get_user_model().objects.all()
            )  # Define user serializer to use

    class Meta:
        model = Recipe
        fields = (
            'id', 'title', 'ingredients', 'tags', 'time_minutes',
            'price', 'link', 'user'  # Add user here
        )
        read_only_fields = ('id',)
var data = {
    title: 'title',
    tags: [],
    ingredients: [],
    time_minutes: 25,
    price: 10.5,
    user: 1,  // Add the user to our javascript data
};

Note, this is hardcoded, so we're assuming there's a user with id of 1.

Finally, you think you're done, you test it, and you get the following response from Django:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

CSRF (or Cross-Site Request Forgery) tokens give your site some added security with any requests that could modify your database (read more here). This should always be used to protect your users (and you!). Django has CSRF tokens built-in to the template tags, so we can add this to our ajax request as a header.

  var headers = {'X-CSRFToken': '{{ csrf_token }}' };  // Inject our token into the javascript using a template tag

  $.ajax({
       type: 'POST',
       data: data,
       headers: headers,  // Set the headers in the request
       url: '/api/recipe/recipes/',
       // ...
   });

You should now get a response from Django with a success:

{
    "id": 1,
    "title": "title",
    "ingredients": [],
    "tags": [],
    "time_minutes": 25,
    "price": "10.50",
    "link": "",
    "user": 1
}

Further Improvements

We have something that works, but it's not very functional. The first thing to do is use the currently logged in user as the recipe's user field. To do this we need to update our recipe serializer to use a default for the user:

    user = serializers.PrimaryKeyRelatedField(
        queryset=get_user_model().objects.all(),
        default=serializers.CurrentUserDefault()  # This field
    )

Now we can remove the user from our ajax data and DRF will use our logged-in user instead, there are other ways of doing this, but this one is quick to implement.

The next thing you could look at implementing is allowing the user to submit tags as a list of strings rather than a list of primary keys. If the tag exists, use it, if not create a new one. You will need to use the DRF ViewSet actions for this though!

Upvotes: 3

Related Questions