django react

I know it’s a very comprehensive title so I’ll keep it as simple as possible.

我知道这是一个非常全面的标题,所以我将使其尽可能简单。

In this article, we will make a book application from installation to the final product using Django and React. I hope to make a nice and simple introduction-development-product series for a CRUD (Create, Read, Update, Delete) application. I want to share the notes I took and the steps I followed while making an app without getting overwhelmed by a lot of information from both sides (back end and front end).

在本文中,我们将使用Django和React从安装到最终产品的制作一个书本应用程序。 我希望为CRUD(创建,读取,更新,删除)应用程序制作一个简单,漂亮的介绍-开发-产品系列。 我想分享我在制作应用程序时所采取的笔记和所采取的步骤,而又不会因为双方(后端和前端)的大量信息而感到不知所措。

I will try not to dwell on the questions such as what and why, but to explain it purposefully. For every command I use, I will cite since there may be people who do not know, in this way you can quickly get an idea from their documentation pages and return to the article.

我将不去赘述诸如什么以及为什么之类的问题,而是有目的地对其进行解释。 我会引用我使用的每个命令,因为可能有些人不知道,这样您就可以从他们的文档页面中快速了解想法并返回本文。

Django is a Python-based free and open-source web library based on the model-template-view architecture.

Django是一个基于Python的免费开源Web库,它基于model-template-view体系结构。

React is a JavaScript library that allows you to create user interfaces for your web projects on a component basis.

React是一个JavaScript库,允许您基于组件为Web项目创建用户界面。

入门#1 (Getting started #1)

Here’s what you need to have installed on your computer before keeping to read the article:

在继续阅读本文之前,您需要在计算机上安装以下文件:

Python3PipNodeJS

While I’m writing this article I use the following versions : Python 3.6~, pip 20.1.1, NodeJS 12.18.2 . If the scripts I use not work or give an error on different versions, please do not hesitate to ask me.

在撰写本文时,我使用以下版本: Python 3.6~ NodeJS 12.18.2pip 20.1.1NodeJS 12.18.2 。 如果我使用的脚本无法正常工作或在不同版本上出现错误,请随时询问我。

Let’s start by setting up a working environment. I created a folder named as DjangoReact. First step is installing pipenv. Pipenv is a tool that brings packaging processes(bundler, npm, yarn, composer, etc.) to python world. It automatically creates and manages a virtualenv for your projects, as well as adds/removes packages from your Pipfile as you install/uninstall packages.

让我们从设置工作环境开始。 我创建了一个名为DjangoReact的文件夹。 第一步是安装pipenv。 Pipenv是一种工具,可将打包过程(捆绑程序,npm,纱线,作曲家等)引入python world。 它会自动为您的项目创建和管理virtualenv,并在您安装/卸载软件包时从Pipfile中添加/删除软件包。

Paste the below code to the command line after going into the workspace:

进入工作区后,将以下代码粘贴到命令行:

python3.6 -m pip install pipenv --upgrade

Afterward, via the below code we’ll install django 3.0.8(latest version) to our virtualenv via the below code:

然后,通过以下代码,我们将通过以下代码将django 3.0.8(最新版本)安装到我们的virtualenv中:

pipenv install --python 3.6 django==3.0.8

After installation is done, our folder tree must be as follows:

安装完成后,我们的文件夹树必须如下所示:

DjangoReact
├── Pipfile
└── Pipfile.lock

To be activated virtualenv run pipenv shell code on the command line.

要激活virtualenv,请在命令行上运行pipenv shell代码。

Now, we can start to create our first Django project. You may see the parameters you could use by typing django-adminon the command line. For now, we just interested in a parameter called startproject. Since we want to install to the folder we are in (we have already created a folder at the beginning of the article), we are creating a project called DjangoReact to the folder we are in.

现在,我们可以开始创建第一个Django项目。 通过在命令行上输入django-admin ,您可能会看到可以使用的参数。 现在,我们只对一个名为startproject的参数startproject 。 由于我们要安装到我们所在的文件夹中(我们已经在本文开头创建了一个文件夹),因此我们要在我们所在的文件夹中创建一个名为DjangoReact的项目。

django-admin startproject DjangoReact .

Here is the final folder tree:

这是最终的文件夹树:

DjangoReact/
manage.py
DjangoReact/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py

I want to take a note here that projects and applications are completely different kinds of things. The application is a web application that serves a specific purpose — blog, todolist, database records, etc. while the project is a collection of applications and configurations within a website. The project can contain more than one application.

我想在此说明项目和应用程序是完全不同的事物。 该应用程序是一个服务于特定目的的Web应用程序-博客,待办事项列表,数据库记录等,而项目则是网站内应用程序和配置的集合。 该项目可以包含多个应用程序。

To check, we run our server and check that it is working properly. When we run the python manage.py runserver code, the output in the terminal should be as follows.

为了进行检查,我们运行服务器并检查它是否正常运行。 当我们运行python manage.py runserver代码时,终端中的输出应如下所示。

(DjangoReact) $~ python manage.py runserverWatching for file changes with StatReloader
Performing system checks…
System check identified no issues (0 silenced).You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run ‘python manage.py migrate’ to apply them.July 17, 2020–14:22:01
Django version 3.0.8, using settings ‘backend.settings’
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

When we enter our localhost, the screen below appears. Yes! It works!

当我们输入本地主机时,将显示以下屏幕。 是! 有用!

Image for post
127.0.0.1
127.0.0.1

One last little touch.

最后一点点触摸。

Image for post

Yes! Now we can add Django to our skills.

是! 现在,我们可以将Django添加到我们的技能中。

Image for post

模型,视图,路由…URL#2 (Model, View, Routing…URLs #2)

Now, it’s time to create our first application. Our to-do list is as follows:

现在,是时候创建我们的第一个应用程序了。 我们的任务清单如下:

1. Books-> Creating-> Name?-> Author?-> Description?-> Image?-> Search-> Delete-> Update2. Feed-> List of all books

We come into DjangoReact folder and create an application with ./manage.py startapp books command. After running the command, it will create a folder named ‘books’ inside our folder and install it. The final version of our folder is as follows:

我们进入DjangoReact文件夹,并使用./manage.py startapp books命令创建一个应用程序。 运行该命令后,它将在我们的文件夹中创建一个名为“ books”的文件夹并进行安装。 我们文件夹的最终版本如下:

DjangoReact/
├── books
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── db.sqlite3
├── DjangoReact
│ ├── asgi.py
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ ├── settings.cpython-36.pyc
│ │ ├── urls.cpython-36.pyc
│ │ └── wsgi.cpython-36.pyc
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── Pipfile
├── Pipfile.lock
└── todo.md

模型 (Model)

We should configure our model.py file in books application as follows.

我们应该按以下方式在books应用程序中配置model.py文件。

from django.db import models
class Books(models.Model):
   # id = models.AutoField(primary_key=True)
   name = models.TextField(blank=False, null=False)
   author = models.TextField(blank=False, null=False)
   description = models.TextField(blank=True, null=True)
   image = models.FileField(upload_to='images/', blank=True, null=True)

With models.TextField(), we define the name, author and description fields. blank = False and null = False fields need to be filled, even if others are empty. Since the id field will increase automatically and will be used as a selector, we have defined it as AutoField and primary key. In order to get our cover images, we determined the file path using FileField and said that this field can be left blank.

使用models.TextField() ,我们定义名称,作者和描述字段。 blank = Falsenull = False字段需要填写,即使其他字段为空。 由于id字段将自动增加并将用作选择器,因此我们将其定义为AutoField和主键。 为了获得封面图像,我们使用FileField确定了文件路径,并说此字段可以留为空白。

迁移 (Migrate)

In order to recognize our application when the project is running, we need to find the INSTALLED_APPS in the settings.py file under our project folder, add and save our model name 'books' . After doing this, we run python manage.py makemigrations command to write the changes we made in our model to the database scheme. After completion, we apply the changes we made with python manage.py migrate command.

为了在项目运行时识别我们的应用程序,我们需要在项目文件夹下的settings.py文件中找到INSTALLED_APPS,添加并保存模型名称'books' 。 完成此操作后,我们运行python manage.py makemigrations命令将我们在模型中所做的更改写入数据库方案。 完成后,我们将应用通过python manage.py migrate命令进行的更改。

$~ python manage.py migrateOperations to perform:
Apply all migrations: admin, auth, books, contenttypes, sessionsRunning migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying books.0001_initial... OK
Applying sessions.0001_initial... OK

Let’s test our model on the terminal and see how it works. Run the shell writing ./manage.py shell and apply the codes below.

让我们在终端上测试我们的模型,看看它是如何工作的。 运行编写./manage.py shell并应用以下代码。

>>> from books.models import Books
>>> obj = Books()
>>> obj.name = "Otostopçunun Galaksi Rehberi"
>>> obj.author = "Douglas Adams"
>>> obj.description = "Uzaylı gören masum köylü" # null veya blank olabilir dediğimiz için bu kısmı boş geçebilirsiniz.
>>> obj.save()
>>> exit()

If you don’t get any error messages re-enter the shell and run the below codes to see either your records have been saved or not.

如果没有收到任何错误消息,请重新输入外壳程序并运行以下代码以查看记录是否已保存。

>>> from books.models import Books
>>> firstBook = Books.objects.get(id=1)
>>> firstBook.name
'Otostopçunun Galaksi Rehberi'
>>> exit()

视图 (View)

Let’s mess with the view side a bit. Open views.py file under our ‘books’ folder and print “Hello World” message to perform a ritual.

让我们把视图端弄乱一点。 打开“ books”文件夹下的views.py文件,并打印“ Hello World”消息以执行仪式。

from django.http import HttpResponse
from django.shortcuts import render
def home_view(request, *args, **kwargs):
   return HttpResponse("<h1>Hello World!</h1>")

And we define the paths to the urls.py file as following:

并且我们定义urls.py文件的路径如下:

from django.contrib import admin
from django.urls import path
from books.views import home_view
urlpatterns = [
   path('admin/', admin.site.urls),
   path('', home_view),
]

I know it’s hard to guess. Who knows what will come across when we enter 127.0.0.1. Yes. It was a joke, a bad one. And now we see “Hello world!” message on our screen again.

我知道这很难猜。 谁知道当我们输入127.0.0.1时会发生什么。 是。 这是一个玩笑,是一个坏玩笑。 现在我们看到“ Hello world!” 消息再次出现在我们的屏幕上。

Image for post

动态网址 (Dynamic URLs)

Let’s create one more function to the view file and print parameters coming with URL.

让我们为视图文件创建另一个函数,并打印URL附带的参数。

def book_detail_view(request, book_id, *args, **kwargs):
   return HttpResponse(f"<h1>Hey {book_id} </h1>")

Of course, there’s no router to run this code. So, add path('books/<int:book_id>', book_detail_view) into urlpatterns. And do not forget to importbook_detail_view from book.view

当然,没有路由器可以运行此代码。 因此,将path('books/<int:book_id>', book_detail_view)到urlpatterns中。 而且不要忘记导入book_detail_viewbook.view

Now, we can see Hey 1 message when we enter http://127.0.0.1:8000/books/1 address. We used url parameter as a string now. Let’s try to use it to bring book information from our database.

现在,当我们输入http://127.0.0.1:8000/books/1地址时,我们可以看到Hey 1消息。 我们现在使用url参数作为字符串。 让我们尝试使用它来从我们的数据库中获取书籍信息。

def book_detail_view(request, book_id, *args, **kwargs):
   obj = Books.objects.get(id=book_id)
   return HttpResponse(f"Kitap Adı: {obj.name} Yazarı: {obj.author}")

It’s easy, isn’t it? Now, when we visit http://127.0.0.1:8000/books/1 address on browser, we’ll see the name and author of a book which its id is 1 in database.

很简单,不是吗? 现在,当我们在浏览器上访问http://127.0.0.1:8000/books/1地址时,我们将在数据库中看到ID为1的一本书的名称和作者。

Image for post

If we enter an id that doesn’t exist in our database, it will show up an error that there’s no data matched with that id. Let’s catch that error up and return a 404 page.

如果我们输入数据库中不存在的ID,则会显示错误,提示没有与该ID匹配的数据。 让我们赶上该错误并返回404页面。

from django.http import HttpResponse, Http404
# import HttpResponse and Http404 from django.http


# in book_detail_view function 
try:
   obj = Books.objects.get(id=book_id)
except:
   raise Http404
####
Image for post
Default 404 page
默认404页面

As I said before, it should be a CRUD application so let’s create a form for the book creation part. Create a file named /templates/components/form.htmland create one more file named form.py under our application folder.

如前所述,它应该是CRUD应用程序,因此让我们为书籍创建部分创建一个表单。 在我们的应用程序文件夹下创建一个名为/templates/components/form.html的文件,并再创建一个名为form.py的文件。

from django import forms
from .models import Books
class BooksForm(forms.modelForm):
   class Meta:
      model = Books
      field = ['name', 'author', 'description', 'image']
   def clean_description(self):
      description = self.cleaned_data.get('description')
      if len(description) > 300:
         raise forms.ValidationError('This is too long')
      return description

We wrote the above code into theform.py file. When we started to our project and created a model, we defined name, author, and description fields as TextField. In the model we created by calling Django’s ‘forms’ class, we want it to create a form based on the data we want. I wanted to give an example of how we can control the data by limiting the book description. I just wanted to take advantage of Django’s benefits.

我们将上面的代码写入了form.py文件。 当我们开始我们的项目并创建模型时,我们将名称,作者和描述字段定义为TextField。 在我们通过调用Django的“表单”类创建的模型中,我们希望它根据所需数据创建表单。 我想举一个例子,说明如何通过限制书籍的描述来控制数据。 我只是想利用Django的优势。

Now, we can use our form in views. Create a function named book_create_view , add it onto urls.py in order to run it when create-book action comes.

现在,我们可以在视图中使用表单了。 创建一个名为book_create_view的函数,将其添加到urls.py以便在执行create-book操作时运行它。

from .form import BooksForm
def book_create_view(request, *args, **kwargs):
   form = BooksForm(request.POST or None)
   if form.is_valid():
      obj = form.save(commit=False)
      obj.save()
      form = BooksForm()
   return render(request, 'components/form.html', 
   context={'form':form})

On the code above, we imported BooksForm from form.py file. While creating a model, we determined which fields should be filled or not. Accordingly, we obtain control from the form class of Django, if there is no data entry other than the expected data, we save the incoming form.

在上面的代码中,我们从form.py文件导入了BooksForm。 在创建模型时,我们确定应填充或不填充哪些字段。 因此,我们从Django的表单类中获取控件,如果除了期望的数据之外没有其他数据输入,则保存传入的表单。

Let’s see our form template on the interface. form.html file as follows:

让我们在界面上查看表单模板。 form.html文件如下:

<form method="POST"> {% csrf_token %}
   {{ form.as_p }}
   <button type="submit">Save</button>
</form>

form.as_p means, wrap all elements between <p> tags. Cross-site request forgery (CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform. To block these kind of attacks, it creates a token and checks it on back-end. This is an digressive of our subject but useful information.

form.as_p表示将所有元素包装在<p>标记之间。 跨站点请求伪造(CSRF)是一个Web安全漏洞,攻击者可以利用该漏洞诱使用户执行他们不打算执行的操作。 为了阻止此类攻击,它将创建令牌并在后端对其进行检查。 这与我们的主题无关,但有用的信息。

I did not want to make an explanation for the deletion and updating process, in order to avoid the word crowd. To delete a data, you can use Books.object.filter(id=book_id).delete() and here is the update method:

我不想解释删除和更新过程,以避免出现“拥挤”一词。 要删除数据,可以使用Books.object.filter(id=book_id).delete() ,这是更新方法:

book = Books.object.get(id=1)
book.name = "Yeni isim"
book.save()

模板,REST API,Djangorestframework ve测试#3 (Templates, REST API, Djangorestframework ve Tests #3)

Now, we will send the data we have in an HTML Template file and print it on the screen more regularly.

现在,我们将发送HTML模板文件中的数据,并更定期地将其打印在屏幕上。

模板 (Template)

First of all, we prepare the template folder. We create a folder named templates in the main directory. In order to determine the template path, we find the TEMPLATESarray in our settings.py file and edit the ‘DIRS’ into [os.path.join(BASE_DIR, "templates")] . Now we have specified our template path. We create an HTML file named /pages/home.html in our templates folder and you can write anything indicating that file is there. I wrote “Hello World from home.html file”. Next, we configure the home_view function in the views section, which we define on the home page, not directly to the screen, but to call our HTML file.

首先,我们准备模板文件夹。 我们在主目录中创建一个名为template的文件夹。 为了确定模板路径,我们在settings.py文件中找到TEMPLATES数组,然后将'DIRS'编辑到[os.path.join(BASE_DIR, "templates")] 。 现在,我们已经指定了模板路径。 我们在模板文件夹中创建一个名为/pages/home.htmlHTML文件,您可以编写任何指示该文件存在的文件。 我写了“来自home.html文件的Hello World”。 接下来,我们在视图部分中配置home_view函数,该函数在主页上定义,而不是直接在屏幕上定义,而是调用HTML文件。

def home_view(request, *args, **kwargs): 
   return render(request, 'pages/home.html', context={} status=200)

To run away from repeated-code let’s create a file named base.html and fill it with the codes we’ll use in all pages such as meta tags, titles, etc.

为了避免重复代码,我们创建一个名为base.html的文件,并在其中填充我们将在所有页面中使用的代码,例如meta标签,标题等。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DjangoReact</title>
   </head>
   <body>
      {% block content %}
      {% endblock content %}
   </body>
</html>

If you have ever used a template engine such as twig and blade, this idea won’t sound strange to you. If you have never used it, you may take a look at the documentation pages and see the basic usage and come back. Basically, the template engines allow us to compile data that comes from the backend in dynamic pages, which we break into pieces — menu, header, meta tags, sidebar, etc. — allows us to run our files according to our needs. I continue without distracting the subject.

如果您曾经使用过诸如树枝和刀片之类的模板引擎,这个想法对您来说听起来并不奇怪。 如果您从未使用过它,则可以查看文档页面,查看基本用法然后再回来。 基本上,模板引擎允许我们在动态页面中编译来自后端的数据,我们将这些数据分成几部分-菜单,标题,元标记,侧边栏等,从而使我们能够根据需要运行文件。 我继续讲,没有分散注意力。

Let’s use our base.html file into home.html as follows:

让我们如下将我们的base.html文件使用到home.html中:

{% extends 'base.html' %}
{% block content %}
   DjangoReact
   <!--- Buraya yazdıklarımız base.html sayfası çağrılarak block content kısmına yerleştirilecek -->
{% endblock content %}
Image for post

As you can see above, when we enter the main page, it now calls our home.html page. When the page is compiled, as I mentioned above, we integrate it into base.html and get an output like this.

如上所示,当我们进入主页时,它现在称为home.html页面。 如前所述,在编译页面时,我们将其集成到base.html中并获得类似的输出。

REST API — DjangoRestFramework (REST API — DjangoRestFramework)

We ran our template and dug a bit. Now it’s better to return our data as json since we use it on front-end. We made our return value as HttpResponse in book_detail_view and home_view. In home_view, we rendered our page over HTML, and in the book_detail_view section, we directly printed it on the screen. We call the JsonResponse class from django.http. We will print our data in a slightly more useful way, rather than directly.

我们运行了模板并进行了一些挖掘。 现在最好将数据作为json返回,因为我们在前端使用了它。 我们在book_detail_view和home_view中将返回值设为HttpResponse。 在home_view中,我们通过HTML渲染页面,在book_detail_view部分中,我们将其直接打印在屏幕上。 我们从django.http调用JsonResponse类。 我们将以一种稍微有用的方式而不是直接地打印数据。

def book_detail_view(request, book_id, *args, **kwargs):
   data = {
       "id" : book_id
   }
   status = 200
   try:
     obj = Books.objects.get(id=book_id)
     data['name'] = obj.name
     data['author'] = obj.author
     data['description'] = obj.description
   except:
     data['message'] = "Not Found"
     status = 404
   return JsonResponse(data,status=status)

In the code above, if searched data exist it returns data object filled with our results. if it’s not it returns an error message. Instead of returning the Http404 when it was not found, we printed an error code and a message on the screen, because we now provide API.

在上面的代码中,如果存在搜索到的数据,它将返回填充有我们结果的数据对象。 如果不是,则返回错误消息。 因为现在提供了API,所以没有在未找到Http404的情况下返回Http404,而是在屏幕上打印了错误代码和消息。

Image for post

Let’s write our code that lists all the books.

让我们编写列出所有书籍的代码。

def books_list(request, *args, **kwargs):
   bist = Books.object.all()
   books = [{"id":x.id, "name":x.name, "author":x.author, "description":x.description} for x in blist]
data = {
        "response" : books
   }
   return JsonResponse(data)

Before moving on to the React section, let’s use the API we created with JavaScript. We don’t always have to use react after all. Our process is to write a very simple JavaScript code. We will send an Http request and print the returned data on the screen. We use the code below to print all the books we have on the homepage as a list.

在进入React部分之前,让我们使用通过JavaScript创建的API。 毕竟,我们不必总是使用react。 我们的过程是编写一个非常简单JavaScript代码。 我们将发送Http请求,并将返回的数据打印在屏幕上。 我们使用下面的代码将主页上所有的书籍作为列表打印。

{% extends 'base.html' %}
{% block content %}
  <h1>DjangoReact</h1>
  <div id="books"></div>
  <script>
    const booksElement = document.getElementById('books')
    booksElement.innerHTML = 'Just a moment'
    const xhr = new XMLHttpRequest()
    const method = 'GET' // or POST
    const url = '/books/'
    const responseType = 'json'
    xhr.responseType = responseType
    xhr.open(method, url)
    xhr.onload = function(){
      const urlResponse = xhr.response
      const Items = urlResponse.response
      let finalExecution = "" 
      let i;
      for(i = 0; i < Items.length; i++){
        let currentItem = "<h3>" + Items[i].name + "</h3>"
        currentItem += "<h4>" + Items[i].author + "<h4>"
        currentItem += "<p>" + Items[i].description + "</p>"
        finalExecution += currentItem 
      }
    booksElement.innerHTML = finalExecution
    }
xhr.send()
</script>
{% endblock content %}

I didn’t do anything about styling because our aim is to understand the main idea.

我没有做任何关于样式的事情,因为我们的目的是理解主要思想。

We did book creation page with book_create_view function and it creates a form according to model rules. Let’s create our own form and send it to API.

我们使用book_create_view函数完成了书创建页面,并根据模型规则创建了一个表单。 让我们创建自己的表单并将其发送到API。

<form method="POST" action="/create-book">
   <input type="hidden" value="/" name="next"/>
   <input type="text" name="name" />
   <input type="text" name="author" />
   <textarea type="text" name="description"></textarea>
   <button type="submit">Save</button>
</form>

After adding the form into home page, fill the form and click the save button. Yes, you see an error message as CSRF verification failed. Request aborted.

将表单添加到主页后,填写表单并单击保存按钮。 是的,由于CSRF verification failed. Request aborted. ,您会看到一条错误消息CSRF verification failed. Request aborted. CSRF verification failed. Request aborted.

When we created our first form we sent a csrf_token. We should apply the same thing here also. We add our {% csrf_token %}code inside the form. After adding it, we made our form working.

创建第一个表单时,我们发送了一个csrf_token。 我们也应该在这里应用相同的内容。 我们在表单内添加{% csrf_token %}代码。 添加完后,我们使表格生效。

Now it stays in the same page after saved our form but it’s better to redirect to the previous page. We already prepared our plan in the above code by adding an input named next. We’ll use that as a redirection page if creation is done. Let’s call the redirect argument from the django.shortcuts library to our view page ( from django.shortcuts import render, redirect). We will use our incoming ‘next ’ value as the page to be redirected and, if the recording has occurred, we will redirect it.

保存表单后,它现在保留在同一页面上,但是最好重定向到上一页。 通过添加名为next的输入,我们已经在上面的代码中准备了计划。 如果创建完成,我们将其用作重定向页面。 让我们将重定向参数从django.shortcuts库调用到我们的视图页面( from django.shortcuts import render, redirect )。 我们将使用传入的“ next”值作为要重定向的页面,如果发生了记录,我们将对其进行重定向。

def book_create_view(request, *args, **kwargs):
  form = BooksForm(request.POST or None)
  next_url = request.POST.geT('next') or None
  if form.is_valid()
    obj = form.save(commit=False)
    obj.save()
    if next_url != None:
      return redirect(next_url)
    form = BooksForm()
  return render(request, 'components/form.html', context={'form': form}

n order to take a little precaution, let’s make sure that the address to be routed is secure. If somehow a different address is sent inside as the next value, the user will not be directed to an unwanted address. To do it, let’s add our secure addresses to the settings.py file with the ALLOWED_HOSTS array.

n为了采取一些预防措施,请确保要路由的地址是安全的。 如果以某种方式将另一个地址作为下一个值发送到内部,则不会将用户定向到不需要的地址。 为此,让我们使用ALLOWED_HOSTS数组将安全地址添加到settings.py文件中。

ALLOWED_HOSTS = ['127.0.0.1', 'yourdomainname.com', 'localhost']

To be able to check these addresses, we’ll use Django’s features. Import the safe_url from django.utils.http import is_safe_url code to the page. This code will check the page which wanted to redirect is safe or not by comparing the URLs in ALLOWED_HOSTS and the redirection page.

为了能够检查这些地址,我们将使用Django的功能。 from django.utils.http import is_safe_urlfrom django.utils.http import is_safe_url代码from django.utils.http import is_safe_url页面。 此代码将通过比较ALLOWED_HOSTS中的URL和重定向页面来检查要重定向的页面是否安全。

We add another check to the line where we control if next_url != None and save it as if next_url != None and is_safe_url(next_url) . When we run this form and submit form, we will see an error, because we did not call our settings to the page and show our accepted addresses. We import our settings to our page with from django.conf import settings . Within the page, we define ALLOWED_HOSTS = settings. We define ALLOWED_HOSTS variable and send it as the second parameter into is_safe_url() (next_url, ALLOWED_HOSTS) .The final version of our view code is as follows:

我们在控制if next_url != None的行中添加另一个检查,并将其保存为if next_url != None and is_safe_url(next_url) 。 当我们运行此表单并提交表单时,会看到一个错误,因为我们没有在页面上调用设置并显示我们接受的地址。 我们使用from django.conf import settings到页面。 在页面中,我们定义ALLOWED_HOSTS =设置。 我们定义ALLOWED_HOSTS变量并将其作为第二个参数发送到is_safe_url()(next_url,ALLOWED_HOSTS)中。我们的视图代码的最终版本如下:

def book_create_view(request, *args, **kwargs):
  form = BooksForm(request.POST or None)
  next_url = request.POST.geT('next') or None
  if form.is_valid()
    obj = form.save(commit=False)
    obj.save()
    if next_url != None and is_safe_url(next_url):
      return redirect(next_url)
    form = BooksForm()
  return render(request, 'components/form.html', context={'form': form}

To test our safe_url code, you may change any other address as next value via form.

要测试我们的safe_url代码,您可以通过表格将其他任何地址更改为下一个值。

序列化器 (Serializer)

The serialization process is the process of converting the class or objects that we have into the format to be stored or sent. In cases where we do not know the types of objects or classes we have registered, we can increase the usability for later use by serializing. This process will bring increased performance and our data to shrink.

序列化过程是将我们拥有的类或对象转换为要存储或发送的格式的过程。 如果我们不知道已注册的对象或类的类型,则可以通过序列化来增加以后使用的可用性。 此过程将带来更高的性能,并且我们的数据将减少。

Let’s install djangorestframework, the django library which will enable us to play on the REST API and help us with serialization, by writing pipenv install djangorestframework code on the console and install it in our project. Add rest_framework to our INSTALLED_APPS array in settings.py file. We can now use this library by calling our page.

让我们安装djangorestframework,这是django库,通过在控制台上编写pipenv install djangorestframework代码并将其安装在我们的项目中,它将使我们能够在REST API上播放并帮助我们进行序列化。 在settings.py文件rest_framework添加到我们的INSTALLED_APPS数组中。 现在,我们可以通过调用页面来使用该库。

Actually, we did the serialization process since we definebooks = [{'id':x.id, 'name':x.name, 'author':x.author, 'description': x.description} for x in blist] and now separating this process let’s add some little changes.

实际上,由于我们books = [{'id':x.id, 'name':x.name, 'author':x.author, 'description': x.description} for x in blist]定义了books = [{'id':x.id, 'name':x.name, 'author':x.author, 'description': x.description} for x in blist] ,所以我们进行了序列化过程。 books = [{'id':x.id, 'name':x.name, 'author':x.author, 'description': x.description} for x in blist]然后分离此过程,让我们添加一些小的更改。

from django.db import models


# Create your models here.
class Books(models.Model):
    # id = models.AutoField(primary_key=True)
    name = models.TextField(blank=False, null=False)
    author = models.TextField(blank=False, null=False)
    description = models.TextField()
    image = models.FileField(upload_to='images/', blank=True, null=True)
    
    def serialize(self):
        return {
            "id" : self.id,
            "name": self.name,
            "author": self.author,
            "description" : self.description,
            "image" : self.image
        }

After defining serialize function inside of Model, we can clarify books_list view.

在Model内部定义了序列化函数后,我们可以澄清books_list视图。

def books_list(request, *args, **kwargs):
  blist = Books.objects.all()
  books = [x.serialize() for x in blist]
  data = {
    'response' : books
  }
  return JsonResponse(data)

Now we have got rid of the code clutter we had while defining our variable.

现在,我们摆脱了定义变量时的代码混乱情况。

We did book creation process by using Form before. Now let’s rewrite it with the serialization process in djangorestframework. Create a file named serializer.py and adapt the code we wrote in form.py here.

我们之前通过使用Form进行过书的创建过程。 现在,让我们使用djangorestframework中的序列化过程重写它。 创建一个名为serializer.py的文件,并修改此处在form.py中编写的代码。

from django.conf import settings 
from rest_framework import serializers
from .models import Books




class BooksSerializer(serializers.ModelSerializer):
    class Meta:
        model = Books
        fields = ['name','author','description']


    def validate_description(self, value):
        if len(value) > 300:
            raise serializers.ValidationError("This is too long")
        return value
from django import forms 


from .models import Books


class BooksForm(forms.ModelForm):
    class Meta:
        model = Books
        fields = ['name','author','description']
    def clean_description(self):
        description = self.cleaned_data.get('description')
        if len(description) > 300:
            raise forms.ValidationError("This is too long")
        return description

Serializer file ready-to-use so we’ll use it in view side.

序列化器文件可立即使用,因此我们将在视图侧使用它。

from .serializer import BooksSerializer


def book_crete_view(request, *args, **kwargs):
    serializer = BooksSerializer(data=request.POST or None)
    if serializer.is_valid():
        serializer.save()
        return JsonResponse(serializer.data, status=200=
    return JsonResponse({}, status=400)


def book_create_view_pure(request, *args, **kwargs):
    form = BooksForm(request.POST or None)
    next_url = request.POST.get('next') or None
    if form.is_valid():
        obj = form.save(commit=False)	   
        obj.save()
        if next_url != None and is_safe_url(next_url,ALLOWED_HOSTS):
            return redirect(next_url)
        form = BooksForm()	        
    return render(request, 'components/form.html', context={'form': form})

I changed book_create_view function we created earlier by adding _pure. I rewrote this function with the serializer I created. Previously we had rendered an html file. Now, we have printed our return result as json to be able to use the results on the front-end side.

我通过添加_pure更改了我们先前创建的book_create_view函数。 我用我创建的序列化器重写了此功能。 以前我们已经渲染了一个html文件。 现在,我们将返回结果打印为json以便能够在前端使用结果。

Let’s roll out from Django Views to Django Rest Framework Views starting from changing book_create_view function.

让我们从更改book_create_view函数开始,从Django Views扩展到Django Rest Framework Views。

@api_view(['POST'])
def book_create_view(request, *args, **kwargs):
   serializer = BooksSerializer(data=request.POST or None)
   if serializer.is_valid(raise_exception = True):
      serializer.save()
      return Response(serializer.data)
   return Response({}, status=400)

Adding the@api_view() code above our function, we provide the function view as an API. We can determine the accessibility of this function by giving parameters such as POST-GET. Our return method is Response instead of JsonResponse now.

在函数上方添加@api_view()代码,我们将函数视图作为API提供。 我们可以通过提供诸如POST-GET之类的参数来确定此功能的可访问性。 我们的返回方法是Response而不是JsonResponse。

That’s all! — not, of course. Well, we’ve learned what we need to keep practicing so far. To learn more about Djangorestframework you may visit its documentation page, you can learn which parameters you can use.

就这样! —当然不是。 好了,到目前为止,我们已经了解了继续练习所需要的知识。 要了解有关Djangorestframework的更多信息,请访问其文档页面,您可以了解可以使用哪些参数。

测验 (Tests)

The last operation will be done on django side is testing. Even you have never done testing before, it’s time to take a step to TDD(Test Driven Development). If we get used to doing this — even if it’s in another language — it will save us time.

最后的操作将在Django端进行测试。 即使您以前从未进行过测试,也该是迈向TDD(测试驱动开发)的时候了。 如果我们习惯于这样做(即使是另一种语言),也可以节省我们的时间。

When we install our application, it brings our test file with it. When we write ./manage.py tests books on the command line, it will run our test and our first output will be as follows:

当我们安装应用程序时,它会附带我们的测试文件。 当我们在命令行上编写./manage.py tests books时,它将运行我们的测试,并且我们的第一个输出如下:

System check identified no issues (0 silenced).----------------------------------------------------------------------
Ran 0 tests in 0.000sOK

The results are incredible, because there is no test code in our file. Hoping to keep this incredibly we keep moving. Let’s write a test code for Books model into test.py file.

结果令人难以置信,因为我们的文件中没有测试代码。 希望我们能继续保持下去。 让我们将Books模型的测试代码写入test.py文件。

from django.test import TestCase
from .models import Books




class BookTestCase(TestCase):
   def test_book_created(self):
       book_obj = Books.objects.create(name='Anonymous', author='John Doe', description='lorem ipsum sit door amet.')
       self.assertEqual(book_obj.id, 1)

In this case, we created a book and when we run the test it will create a new book and will be tested its id. If it did not save it or the data its saved is different than it should be, it will show up an error message.

在这种情况下,我们创建了一本书,并且在运行测试时它将创建一本新书并对其ID进行测试。 如果它没有保存它或保存的数据与应该保存的数据不同,它将显示一条错误消息。

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.003sOK
Destroying test database for alias 'default'...

It ran successfully. If we have done self.assertEqual(book_obj.id, 5) it would show up an error as AssertionError: 1 != 5 when we run our test. While doing this for the first time, the first data given to database should have id 1, we say check it for 5. The same error would come even if the id of the created object is not 1. You can see more usages of Django’s documentation on testing.

运行成功。 如果我们完成了self.assertEqual(book_obj.id, 5)则在运行测试时,它将显示为AssertionError: 1 != 5的错误。 第一次执行此操作时,提供给数据库的第一个数据应具有ID 1,我们说检查它是否为5。即使所创建对象的ID不为1,也会出现相同的错误。您可以看到Django的更多用法测试文档。

Well, after we did everything how we can deploy our project? In this case, there’s information in almost every service providers’ pages. I’ll give you resources directly since it has no side can be interpreted. You may run Django in cloud-based platforms. The most popular about it is Heroku. You may publish your first project by following its own instructions. If you want to use your own server there are instructions on DigitalOcean. These processes will be exactly same in servers you can access with ssh.

好吧,在完成所有工作之后,我们如何部署项目? 在这种情况下,几乎每个服务提供商的页面中都有信息。 我将直接为您提供资源,因为它没有任何一方可以解释。 您可以在基于云的平台上运行Django。 最受欢迎的是Heroku。 您可以按照自己的说明发布第一个项目。 如果要使用自己的服务器,请参阅DigitalOcean。 在您可以通过ssh访问的服务器中,这些过程将完全相同。

So far, we have completed the operations we will do with Django. Adding books, listing, and printing the required book on the screen. I did not add the repetitive codes to avoid extending the article. Of course, not everything ends with this much. There is an alternative to every line of code we write. Each title goes deeper within itself. Starting with the simplest, we will continue to improve ourselves with plenty of practice. You can see the source codes I use in this article on Github, and contribute if you wish.

到目前为止,我们已经完成了对Django的操作。 在屏幕上添加书本,列出并打印所需的书本。 我没有添加重复代码以避免扩展本文。 当然,并非一切都以这么多结尾。 我们编写的每一行代码都有替代方法。 每个标题本身都有更深的含义。 从最简单的开始,我们将通过大量实践不断提高自己。 您可以在Github上查看我在本文中使用的源代码,并根据需要提供帮助。

You can contact me if you have any questions or about anything you need via my Twitter account.

如果您有任何疑问或需要任何东西,可以通过我的Twitter帐户与我联系。

I also have another article which is a continuation of this article named Full-Stack with Django and React — React.

我还有另一篇文章,是该文章的继续,名为Django和React — React的Full-Stack

Happy codding! :)

祝大家高兴! :)

Github, Twitter, Instagram: @bari5d

Github,Twitter,Instagram:@ bari5d

翻译自: https://medium.com/swlh/full-stack-with-django-and-react-django-4dcd87d57356

django react

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐