Python Automation Development Learning 23-django (Form)

Source: Internet
Author: User
Tags script tag serialization

Review

The main function of form is to do data validation. and the data validation function of the form is powerful.
The form also has another function, which is to help me generate HTML tags.
The above 2 features, where validation is the primary, and the ability to generate HTML tags are sometimes used, sometimes not required. It is recommended to use the ability to generate HTML tags when using the new URL method (usually form form submission), because this feature can help me retain the value of the last commit. Using the AJAX request operation, you can not use the form to help us generate HTML tags, because the AJAX submission page will not be refreshed, the page will not lose the value of the last commit. The above is recommended, the specific use of the time can try, feel not as suggested may be possible, but at least a little trouble.
Lecturer's blog Address: http://www.cnblogs.com/wupeiqi/articles/6144178.html

Create a form

We recommend creating a new forms.py file in the app directory to store our form files separately:

from django.forms import Formfrom django.forms import fieldsfrom django.forms import widgetsclass User(Form):    username = fields.CharField(        required=True,  # 必填,不能为空,不过True是默认参数,所以不写也一样        widget=widgets.PasswordInput(attrs={‘class‘: ‘c1‘})  # 在生成html标签的时候,添加标签的属性    )    password = fields.CharField(        max_length=12,        widget=widgets.PasswordInput(attrs={‘class‘: ‘c1‘})    )
Action generates a form for the Select label
# 在 models.py 文件了创建表结构class UserType(models.Model):    name = models.CharField(max_length=16)  # 这个是用户类型,比如:普通用户、超级用户# 在 forms.py 文件里创建Formclass UserInfo(Form):    name = fields.CharField()    comments = fields.CharField(        required=False,        widget=widgets.Textarea(attrs={‘class‘: ‘c1‘})    )    type = fields.ChoiceField(        # choices=[(1, "超级用户"), (2, "普通用户")]  # 之前是去内存里的选项,想在用下面的方法直接去数据库里取        # 列表里每一个元素都是元组,如果选项是去数据库获取的话,用.values_list()方法就能直接获取到        choices=models.UserType.objects.values_list()    )# 在 views.py 里创建视图函数def user_info(request):    obj = forms.UserInfo()    return render(request, ‘user_info.html‘, {‘obj‘: obj})

Then write a simple HTML interface, mainly look at the generated select tag:

<body><p>{{ obj.name }}</p><p>{{ obj.comments }}</p><p>{{ obj.type }}</p></body>

The direct use of the choices=[(1, "超级用户"), (2, "普通用户")] words must be no problem, this is the content of the previous study.
It's also a good way to go directly to the database, because the type of data you get is the list of tuple elements and the option to generate a select tag. But here's a problem, like restarting our Django, and the new option will take effect on the page.
The advantage of putting the option in memory is that it's convenient to use, and for those options that won't change after you set it up, you'll need to restart Django after you've adjusted it. If the option needs to be dynamically adjusted, then put it in the database. But it's useless to use the above, because we still have to restart Django before it takes effect. See the following section for specific usage.

Manipulating Dynamic Select data

The reason why the non-restart cannot be updated above is that we use static fields to define it. Before learning object-oriented, that is also called the public attribute. A static field is used to address this.
The first time the program is loaded, a static field is executed, and the contents of the database are saved in memory by a static field. After the request, it will not go back to the database to obtain, but in memory to find the data obtained before. This results in the inability to dynamically update the latest content in the database.

By constructing a method to implement

The data displayed by the front-end HTML is passed through the view function's obj. In the view function, after getting to the static field, return to the front end, go back to the database to get the latest data, overwriting the previous static data. :

# views.py 文件def user_info(request):    obj = forms.UserInfo()  # 这步是实例化,obj里有所有静态字段的静态的内容    # 实际上,所有的静态字段都在obj.fields里。下面就把其中的type字段里的choices的值重新去获取一下    obj.fields[‘type‘].choices = models.UserType.objects.values_list()  # 这里是重新去数据库里再获取一下最新的值    return render(request, ‘user_info.html‘, {‘obj‘: obj})

However, the above method is not good, because this implementation, to use this field every time you write more such a sentence. Here is the modification of the froms, the value of each instantiation of the time to go back to the database to get once, the specific practice is put in the construction method to execute.
Just to get a sense of what's going on, here's how we end up using it:

# forms.py 文件class UserInfo(Form):    name = fields.CharField()    comments = fields.CharField(        required=False,        widget=widgets.Textarea(attrs={‘class‘: ‘c1‘})    )    type = fields.ChoiceField(        # choices=[(1, "超级用户"), (2, "普通用户")]        # 列表里每一个元素都是元组,如果选项是去数据库获取的话,用.values_list()方法就能直接获取到        # choices=models.UserType.objects.values_list()        choices=[]  # 这个值具体是多少,在这里不重要了,每次实例化的时候都会在构造方法里重新赋值    )    def __init__(self, *args, **kwargs):        super(UserInfo, self).__init__(*args, **kwargs)  # 仅有这一行,就是完全继承父类的构造方法        # 下面我们再加上我们在构造方法中要额外执行的代码        self.fields[‘type‘].choices = models.UserType.objects.values_list()  # 从之前的视图函数,搬到这里

If you are using the Charfield widget plugin to define the Select, then the key 2 lines are as follows:

# 使用CharField来定义selecttype2 = fields.CharField(widget=widgets.Select(choices=[]))# 构造函数里赋值的方法self.fields[‘type2‘].widget.choices = models.UserType.objects.values_list()
Implemented through Modelchoicefield

There is also a Django-provided method, but it cannot be used alone, and a special method is constructed in the model to implement the value of the display field instead of the object itself:

    from django.forms.models import ModelChoiceField    type3 = ModelChoiceField(        queryset=models.UserType.objects.all()    )

This can also dynamically update the values in the database, but now the page shows that the object is not the database table value. We're going to have to go to the models. Constructs a __str__ method that defines what is printed and processed when the object is printed:

class UserType(models.Model):    name = models.CharField(max_length=16)    def __str__(self):        return self.name

The above situation may also be encountered in the admin operation. In short, if the page is not displayed on the value of the field but the object, it is to go to class construction this special method should be able to solve.
There are other parameters to this function:

    • queryset: It says, get the choices option.
    • empty_label="---------": The default option displays the content
    • to_field_name=None: The field of value in HTML corresponds to the value of the ID, which is the same as the assignment ' id ' effect.
    • limit_choices_to=None: Modelform to Queryset two times in the filter
      There should be other parameters, which are more useful
      Explicitly, the value of the text of the option is the value __str__ returned by the method, and the value of the option is the value of the field defined by the To_field_name.
      Above is a single, if you need more choice, use the following method to replace it.
      ModelMultipleChoiceField(ModelChoiceField)

      Finally this method is not completely in the form of implementation, but also to write a special method models, so the teacher conditions with the above method. All are implemented in the form.

Form built-in hooks

First add the previous code complete, the HTML does not have a form, and did not commit, here also turned off the form front-end verification function (novalidate= "Novalidate"):

<body><form action="." novalidate="novalidate" method="post">    {% csrf_token %}    <p>{{ obj.name }}</p>    <p>{{ obj.comments }}</p>    <p>{{ obj.type }}</p>    <p>{{ obj.type3 }}</p>    <p><input type="submit"></p></form></body>

Then write the processing function complete, now have get and post two methods:

def user_info(request):    if request.method == ‘GET‘:        obj = forms.UserInfo({‘name‘: ‘Your Name‘})  # 这步是实例化,obj里有所有静态字段的静态的内容        # 实际上,所有的静态字段都在obj.fields里。下面就把其中的type字段里的choices的值重新去获取一下        # obj.fields[‘type‘].choices = models.UserType.objects.values_list()  # 这里是重新去数据库里再获取一下最新的值        return render(request, ‘user_info.html‘, {‘obj‘: obj})    elif request.method == ‘POST‘:        obj = forms.UserInfo(request.POST)  # 获取POST提交的数据        res = obj.is_valid()  # 这一步是进行验证        if res:            print(obj.cleaned_data)            return HttpResponse(str(obj.cleaned_data))        else:            print(obj.errors)            return HttpResponse(str(obj.errors))  # 通过str方法后,页面上会直接按html代码处理
Find the hooks.

This is the first res = obj.is_valid() step to verify. This one will execute self.errors . Take a look at this errors method, inside will execute one self.full_clean() . Finally came the Full_clean method, the complete code is as follows:

    def full_clean(self):        """        Clean all of self.data and populate self._errors and self.cleaned_data.        """        self._errors = ErrorDict()        if not self.is_bound:  # Stop further processing.            return        self.cleaned_data = {}        # If the form is permitted to be empty, and none of the form data has        # changed from the initial data, short circuit any validation.        if self.empty_permitted and not self.has_changed():            return        self._clean_fields()        self._clean_form()        self._post_clean()

Here's the last 3 lines.

The method of reconstructing hooks

All hook methods are refactored in our own form class.
self._clean_fields()Each field is validated separately
There are a few lines of content in the following:

                if hasattr(self, ‘clean_%s‘ % name):                    value = getattr(self, ‘clean_%s‘ % name)()                    self.cleaned_data[name] = value

Determine if there is clean_%s a method, if any, to execute. corresponding to our current UserInfo(Form) class, we can individually customize Clean_name clean_comments These methods, from custom validation rules.
self._clean_form()Validation of the whole
There is a direct call to an empty method, self.clean() this is directly left to our hooks. Refactor this method directly in the class to use. The most common scenario for overall validation is to verify the login username and password.
self._post_clean()Additional validation
This is directly an empty method, so it's good to refactor it directly. There is no expansion here, should use the above 2 is enough. This method is and self._clean_fields() , self._clean_form() at a level, the 2 ways to help me write the try and then call the hook in the try. And this method is completely customizable, and nothing is written.
Add the following code to the end of the UserInfo class, adding custom validation:

# forms.py 文件from django.core.exceptions import ValidationError  # 导入异常类型class UserInfo(Form):    # 前面的代码不变,先把钩子方法写上,稍后再来完善    def clean_name(self):        """单独对每个字段进行验证"""        name = self.cleaned_data[‘name‘]  # 获取name的值,如果验证通过,直接返回给clean_data        if name == ‘root‘:            raise ValidationError("不能使用“root”做用户名")        return name    def clean_comments(self):        # 这里可以再补充验证的内容        return self.cleaned_data[‘comments‘]    def clean(self):        """对form表单的整体内容进行验证"""        # 如果字段为空,但是验证要求不能为空,那么在完成字段验证的时候,cleaned_data里是没有name这个键值的        # 但是会继续往下进行之后的验证,下面要用get,否则没有这个键的话会报错        name = self.cleaned_data.get(‘name‘)        comments = self.cleaned_data.get(‘comments‘)        if name == comments:            raise ValidationError("用户名和备注的内容不能一样")        return self.cleaned_data    def _post_clean(self):        pass
Other supplemental content

Here ValidationError the exception type, the first parameter message is the error message, which is already available. The second parameter, code, is an error type, and there is no definition, and it seems to have no effect. Before learning the form, custom error messages have been used to modify the native error information to a custom error message based on the error type. For example: "Required", "Max_length", "Min_length".
In the views processing function, all the error messages are stored in the obj.errors. where field validation error information is the key value is the field name. And then the overall validation of the resulting error message is stored in a special key value __all__ . In addition, you may see the __all__ following constants instead of directly writing them:

from django.core.exceptions import NON_FIELD_ERRORS# 在源码是这这样定义的NON_FIELD_ERRORS = ‘__all__‘

So __all__ , when you see it, recognize it.

Serialization error message-with Ajax

This is what was last mentioned.
First, prepare the HTML page for the front end, or use the user_info.html above. There is no need to change the front, just append jquery code in the back, here are all posted:

 <body><form action= "." Novalidate= "Novalidate" method= "post" > {% csrf_ Token%} <p>{{obj.name}}</p> <p>{{obj.comments}}</p> <p>{{obj.type}}</p>    ; <p>{{obj.type3}}</p> <p><input type= "submit" ></p></form>{% load static%}< Script src= "{% static" Jquery-1.12.4.min.js "%}" ></script><script> $ (function () {$ (' form '). "On ("  Submit ", function (event) {Event.preventdefault (); To block the default action, we want to perform our own Ajax operation $.ajax ({url: '. ', type: ' POST ', Data: $ (                This). Serialize (), Success:function (ARG) {Console.log (ARG)}, Error:function (ARG) {alert ("Request execution failed")})}) </script ></body>  

The submit binding event for form forms in jquery above first blocks the default action of form, which is the submit submission of the form form. Then perform the Ajax operation that you wrote. The
forms.py has written a lot of verification before, and can use it directly. The get in the
views.py does not have to be changed. The main change is what the post returns. This is what is being said here.

def user_info(request):    if request.method == ‘GET‘:        obj = forms.UserInfo({‘name‘: ‘Your Name‘})  # 这步是实例化,obj里有所有静态字段的静态的内容        # 实际上,所有的静态字段都在obj.fields里。下面就把其中的type字段里的choices的值重新去获取一下        # obj.fields[‘type‘].choices = models.UserType.objects.values_list()  # 这里是重新去数据库里再获取一下最新的值        return render(request, ‘user_info.html‘, {‘obj‘: obj})    elif request.method == ‘POST‘:        obj = forms.UserInfo(request.POST)  # 获取POST提交的数据        res = obj.is_valid()  # 这一步是进行验证        if res:            # 验证通过的情况,之前已经会处理了。这里主要关注下面验证不通过,如何返回错误信息            print(obj.cleaned_data)            return HttpResponse(str(obj.cleaned_data))        else:            print(type(obj.errors))  # 先来看看这个错误信息的类型            return HttpResponse(str(obj.errors))  # 通过str方法后,页面上会直接按html代码处理

Let's take a print(type(obj.errors)) look at the type of variable that holds the error message, and the print results are as follows:

<class ‘django.forms.utils.ErrorDict‘>

Here still try to find it in the source code, explore. In the Pycharm write the following sentence, and then CTRL + Left button, you can quickly locate the source code:

from django.forms.utils import ErrorDict

Seemingly not much content, all posted below:

@html_safeclass errordict (dict): "" "A collection of errors that knows what to display itself in various formats.    The dictionary keys is the field names, and the values are the errors. "" "Def As_data (self): return {f:e.as_data () for F, E in Self.items ()} def get_json_data (self, escape_html=f        Alse): Return {f:e.get_json_data (escape_html) for F, E in Self.items ()} def as_json (self, escape_html=false):        Return Json.dumps (Self.get_json_data (escape_html)) def as_ul (self): if not Self:return ' Return format_html (' <ul class= ' errorlist ' >{}</ul> ', Format_html_join (', ' <li> {}{}</li> ', Self.items ())) def as_text (self): output = [] for field, errors in Self.items ()  : Output.append (' *%s '% field) output.append (' \ n '. Join (' *%s '% E for E in errors)) return ' \ n '. Join (output) def __str__ (self): return Self.as_ul ()

Before the direct printing process is his As_ul method returns the value, see the bottom of the special method __str__ .

2 serialization operations with the As_json () method

Here's a look at the As_json () method first. The processing function is modified to the following:

def user_info (Request): if Request.method = = ' GET ': obj = forms. UserInfo ({' name ': ' Your Name '}) # This step is instantiated, and obj has static content for all static fields # In fact, all static fields are in Obj.fields. The following is the value of the choices in the Type field to get back to the # obj.fields[' type '].choices = models. UserType.objects.values_list () # Here's to go back to the database and get the latest value return render (Request, ' user_info.html ', {' obj ': obj}) Eli F Request.method = = ' POST ': ret = {' status ': True, ' Errors ': None, ' Data ': none} obj = forms. UserInfo (Request. Post) # Gets the data of the post submission res = Obj.is_valid () # This step is to verify if Res: # validation passed the case that was already processed before. Here is the main focus on the following verification does not pass, how to return error message print (obj.cleaned_data) Else: # print (Type (obj.errors)) # First Look at this error message            Type ret[' status '] = False ret[' errors '] = Obj.errors.as_json () # As_json () method, the information about the error is carried out dumps Print (ret) # Return at the time of the full data is once again dumps, so the front end to obtain errors information at the time also to reverse 2 times return HttpResponse (Json.dumps ( RET))

The contents of the script tag in the front end are modified to:

<script>    $(function () {        $(‘form‘).on("submit", function (event) {            event.preventDefault();  // 阻止默认操作,我们要执行自己的Ajax操作            $.ajax({                url: ‘.‘,                type: ‘POST‘,                data: $(this).serialize(),                success: function (arg){                    console.log(arg);                    var obj = JSON.parse(arg);                    if (obj.status){                        location.reload()                    } else {                        var errors = JSON.parse(obj.errors);  // 这里是第二次反解了,返回的error里的数据要反解2次                        console.log(errors)                    }                },                error: function (arg) {                    console.log("请求执行失败")                }            })        })    })</script>

For the time being, only the error message is output to the console. There is still a problem here, because JSON is being reversed two times.
This is a stupid way to achieve the demand, but certainly not good

Use the As_data () method to serialize a custom method

Now look at the As_data () method. The print(type(obj.errors.as_data())) type of data returned by this method can be viewed is the native dictionary type. Again to print the contents of the dictionary print(obj.errors.as_data()) , you can see that the dictionary nested other data types, is not directly serialized:

{‘type3‘: [ValidationError([‘未选择你的选项‘])], ‘__all__‘: [ValidationError([‘用户名和备注的内容不能一样‘])]}

For the ValidationError type, we only need the messages and code, respectively, the error message and the type of error. There is a need to customize the class to serialize instead of the dumps method:

from django.core.exceptions import ValidationErrorclass JsonCustomEncoder(json.JSONEncoder):    def default(self, field):        if isinstance(field, ValidationError):            return {‘code‘: field.code, ‘messages‘: field.messages}        else:            return json.JSONEncoder.default(self, field)

With the above class, it is now only necessary to serialize and deserialize once:

ret[‘status‘] = Falseret[‘errors‘] = obj.errors.as_data()result = json.dumps(ret, cls=JsonCustomEncoder)  # 这里的dumps方法多了一个cls参数return HttpResponse(result)

About the dumps method used above. The method used in serialization is defined in the CLS. The above custom class inherits the default method and then writes the additional processing methods that we need to serialize.
This method does not need to be serialized 2 times, but with the following method, this is not required. But with this little piece of code, I learned the advanced knowledge of JSON serialization operations.

2.0 new methods through the Get_json_data () method

This is the official website connection: https://docs.djangoproject.com/en/2.0/ref/forms/api/
The main is to go inside to confirm the following information:

This method is Django 2.0. New added. So with it, there is no need for either of these two kinds of trouble implementations.
Here is a comparison with the As_data () method:

temp1 = obj.errors.get_json_data()print(type(temp1), temp1)temp2 = obj.errors.as_data()print(type(temp2), temp2)

The following is a comparison of the printed results:

<class ‘dict‘> {‘type3‘: [{‘message‘: ‘This field is required.‘, ‘code‘: ‘required‘}], ‘__all__‘: [{‘message‘: ‘用户名和备注的内容不能一样‘, ‘code‘: ‘‘}]}<class ‘dict‘> {‘type3‘: [ValidationError([‘This field is required.‘])], ‘__all__‘: [ValidationError([‘用户名和备注的内容不能一样‘])]}

The effect is the same as the above method, which is directly the native dictionary. But here's a straightforward way to use the new native method provided.

Serialization of Queryset

The results of the database query (model operation) are now also serialized back to the front end.
Have never encountered this kind of problem, because it is the template language before the data to the front page. If you want to support the operation through the AJAX request, get the back end of the data, you will use the following serialization method.

Method One
from django.core import serializersobj = models.UserType.objects.all()data = serializers.serialize(‘json‘, obj)print(data)  # 打印结果在下面# [{"model": "app01.usertype", "pk": 1, "fields": {"name": "\u8d85\u7ea7\u7528\u6237"}}]

The Queryset object is returned here. So it can't be serialized with JSON. The serialization is implemented using the method provided by Django. Where model is the table name, PK is the value of the primary key that is the ID. The last fields are the rest of the data.

Method Two
import jsonobj = models.UserType.objects.values()obj = list(obj)  # 直接可以转成一个原生的字典data = json.dumps(obj)print(data)  #打印结果如下# [{"id": 1, "name": "\u8d85\u7ea7\u7528\u6237"}]

This also allows serialization to be completed.
But if there is a date or time type, there will be a problem. This time, referring to the second method of serialization of the above error message, you can customize a serialization method or complete the serialization operation.

Python Automation Development Learning 23-django (Form)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.