Django之Form组件

Django的Form功能

  • 生成HTML标签
  • 验证用户数据(显示错误信息)
  • HTML Form提交保留上次提交数据
  • 初始化页面显示内容

Django form 流程

1、创建类,继承form.Form

2、页面根据类的对象自动创建html标签

3、提交,request.POST

封装到类的对象里,obj=UserInfo(request.POST)

4、用户输入是否合法

obj.is_valid()

5、全部合法,获取全部内容

obj.clean()

6、只有有不合法

obj.errors

1、创建Form类

from django.forms import Form

from django.forms import widgets

from django.forms import fields 

class MyForm(Form):

    user = fields.CharField(

        widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'})

    )

 

    gender = fields.ChoiceField(

        choices=((1, '男'), (2, '女'),),

        initial=2,

        widget=widgets.RadioSelect

    )

 

    city = fields.CharField(

        initial=2,

        widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))

    )

 

    pwd = fields.CharField(

        widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)

    )

2、View函数处理

from django.shortcuts import render, redirect

from .forms import MyForm

 

 

def index(request):

 if request.method == "GET":

        obj = MyForm()

 return render(request, 'index.html', {'form': obj})

 elif request.method == "POST":

        obj = MyForm(request.POST, request.FILES)

 if obj.is_valid():

            values = obj.clean()

            print(values)

 else:

            errors = obj.errors

            print(errors)

 return render(request, 'index.html', {'form': obj})

 else:

 return redirect('http://www.google.com')

3、生成HTML

<form action="/" method="POST" enctype="multipart/form-data">

 <p>{{ form.user }} {{ form.user.errors }}</p>

 <p>{{ form.gender }} {{ form.gender.errors }}</p>

 <p>{{ form.city }} {{ form.city.errors }}</p>

 <p>{{ form.pwd }} {{ form.pwd.errors }}</p>

 <input type="submit"/>

</form>


<form method="POST" enctype="multipart/form-data">

        {% csrf_token %}        

            {{ form.xxoo.label }}

            {{ form.xxoo.id_for_label }}

            {{ form.xxoo.label_tag }}

            {{ form.xxoo.errors }}

 <p>{{ form.user }} {{ form.user.errors }}</p>

 <input type="submit" />

 </form>

Form类

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

1、Django内置字段如下:

Field

    required=True,               是否允许为空

    widget=None,                 HTML插件

    label=None,                  用于生成Label标签或显示内容

    initial=None,                初始值

    help_text='',                帮助信息(在标签旁边显示)

    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}

    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)

    validators=[],               自定义验证规则

    localize=False,              是否支持本地化

    disabled=False,              是否可以编辑

    label_suffix=None            Label内容后缀  

CharField(Field)

    max_length=None,             最大长度

    min_length=None,             最小长度

    strip=True                   是否移除用户输入空白 

IntegerField(Field)

    max_value=None,              最大值

    min_value=None,              最小值 

FloatField(IntegerField)

    ... 

DecimalField(IntegerField)

    max_value=None,              最大值

    min_value=None,              最小值

    max_digits=None,             总长度

    decimal_places=None,         小数位长度

BaseTemporalField(Field)

    input_formats=None          时间格式化    

DateField(BaseTemporalField)    格式:2015-09-01

TimeField(BaseTemporalField)    格式:11:12

DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 

DurationField(Field)            时间间隔:%d %H:%M:%S.%f

    ... 

RegexField(CharField)

    regex,                      自定制正则表达式

    max_length=None,            最大长度

    min_length=None,            最小长度

    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'} 

EmailField(CharField)      

    ...

FileField(Field)

    allow_empty_file=False     是否允许空文件 

ImageField(FileField)          ...

    注:需要PIL模块,pip3 install Pillow

    以上两个字典使用时,需要注意两点:

        - form表单中 enctype="multipart/form-data"

        - view函数中 obj = MyForm(request.POST, request.FILES) 

URLField(Field)

    ... 

BooleanField(Field)  

    ... 

NullBooleanField(BooleanField)

    ... 

ChoiceField(Field)

    ...

    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)

    required=True,             是否必填

    widget=None,               插件,默认select插件

    label=None,                Label内容

    initial=None,              初始值

    help_text='',              帮助提示

ModelChoiceField(ChoiceField)

    ...                        django.forms.models.ModelChoiceField

    queryset,                  # 查询数据库中的数据

    empty_label="---------",   # 默认空显示内容

    to_field_name=None,        # HTML中value的值对应的字段

    limit_choices_to=None # ModelForm中对queryset二次筛选     

ModelMultipleChoiceField(ModelChoiceField)

    ...                        django.forms.models.ModelMultipleChoiceField     

TypedChoiceField(ChoiceField)

    coerce = lambda val: val   对选中的值进行一次转换

    empty_value= ''            空值的默认值 

MultipleChoiceField(ChoiceField)

    ... 

TypedMultipleChoiceField(MultipleChoiceField)

    coerce = lambda val: val   对选中的每一个值进行一次转换

    empty_value= ''            空值的默认值 

ComboField(Field)

    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式

                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) 

MultiValueField(Field)

    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 

SplitDateTimeField(MultiValueField)

    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']

    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] 

FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中

    path,                      文件夹路径

    match=None,                正则匹配

    recursive=False,           递归下面的文件夹

    allow_files=True,          允许文件

    allow_folders=False,       允许文件夹

    required=True,

    widget=None,

    label=None,

    initial=None,

    help_text='' 

GenericIPAddressField

    protocol='both',           both,ipv4,ipv6支持的IP格式

    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 

SlugField(CharField)           数字,字母,下划线,减号(连字符)

    ... 

UUIDField(CharField)           uuid类型

    ...

注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串

>>> import uuid

 

 # make a UUID based on the host ID and current time

    >>> uuid.uuid1()    # doctest: +SKIP

    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

 

 # make a UUID using an MD5 hash of a namespace UUID and a name

    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')

    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')

 

 # make a random UUID

    >>> uuid.uuid4()    # doctest: +SKIP

    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')

 

 # make a UUID using a SHA-1 hash of a namespace UUID and a name

    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')

    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')

 

 # make a UUID from a string of hex digits (braces and hyphens ignored)

    >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')

 

 # convert a UUID to a string of hex digits in standard form

    >>> str(x)

 '00010203-0405-0607-0809-0a0b0c0d0e0f'

 

 # get the raw 16 bytes of the UUID

    >>> x.bytes

 b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'

 

 # make a UUID from a 16-byte string

    >>> uuid.UUID(bytes=x.bytes)

    UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')

2、Django内置插件:

TextInput(Input)

NumberInput(TextInput)

EmailInput(TextInput)

URLInput(TextInput)

PasswordInput(TextInput)

HiddenInput(TextInput)

Textarea(Widget)

DateInput(DateTimeBaseInput)

DateTimeInput(DateTimeBaseInput)

TimeInput(DateTimeBaseInput)

CheckboxInput

Select

NullBooleanSelect

SelectMultiple

RadioSelect

CheckboxSelectMultiple

FileInput

ClearableFileInput

MultipleHiddenInput

SplitDateTimeWidget

SplitHiddenDateTimeWidget

SelectDateWidget

常用选择插件

# 单radio,值为字符串

# user = fields.CharField(

#     initial=2,

#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))

# )

 

# 单radio,值为字符串

# user = fields.ChoiceField(

#     choices=((1, '上海'), (2, '北京'),),

#     initial=2,

#     widget=widgets.RadioSelect

# )

 

# 单select,值为字符串

# user = fields.CharField(

#     initial=2,

#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))

# )

 

# 单select,值为字符串

# user = fields.ChoiceField(

#     choices=((1, '上海'), (2, '北京'),),

#     initial=2,

#     widget=widgets.Select

# )

 

# 多选select,值为列表

# user = fields.MultipleChoiceField(

#     choices=((1,'上海'),(2,'北京'),),

#     initial=[1,],

#     widget=widgets.SelectMultiple

# )

 

# 单checkbox

# user = fields.CharField(

#     widget=widgets.CheckboxInput()

# ) 

# 多选checkbox,值为列表

# user = fields.MultipleChoiceField(

#     initial=[2, ],

#     choices=((1, '上海'), (2, '北京'),),

#     widget=widgets.CheckboxSelectMultiple

# )

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

方式一:

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator 

class MyForm(Form):

 

    user = fields.ChoiceField(

 # choices=((1, '上海'), (2, '北京'),),

        initial=2,

        widget=widgets.Select

    )

 

 def __init__(self, *args, **kwargs):

        super(MyForm,self).__init__(*args, **kwargs)

 # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)

 # 或

        self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

方式二:

使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

from django import forms

from django.forms import fields

from django.forms import widgets

from django.forms import models as form_model

from django.core.exceptions import ValidationError

from django.core.validators import RegexValidator 

class FInfo(forms.Form):

    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())

 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())

自定义验证规则

方式一:

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator

 

class MyForm(Form):

    user = fields.CharField(

        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],

    )

方式二:

import re

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.exceptions import ValidationError 

# 自定义验证规则

def mobile_validate(value):

    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')

    if not mobile_re.match(value):

        raise ValidationError('手机号码格式错误')

 

 

class PublishForm(Form):

 

 

    title = fields.CharField(max_length=20,

                            min_length=5,

                            error_messages={'required': '标题不能为空',

                                            'min_length': '标题最少为5个字符',

                                            'max_length': '标题最多为20个字符'},

                            widget=widgets.TextInput(attrs={'class': "form-control",

                                                          'placeholder': '标题5-20个字符'}))

    # 使用自定义验证规则

    phone = fields.CharField(validators=[mobile_validate, ],

                            error_messages={'required': '手机不能为空'},

                            widget=widgets.TextInput(attrs={'class': "form-control",

                                                          'placeholder': u'手机号码'}))

    email = fields.EmailField(required=False,

                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},

                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

方法三:自定义方法

from django import forms

    from django.forms import fields

    from django.forms import widgets

    from django.core.exceptions import ValidationError

    from django.core.validators import RegexValidator

 

    class FInfo(forms.Form):

        username = fields.CharField(max_length=5,

                                    validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )

        email = fields.EmailField()

 

        def clean_username(self):

            """

            Form中字段中定义的格式匹配完之后,执行此方法进行验证

            :return:

            """

            value = self.cleaned_data['username']

            if "666" in value:

                raise ValidationError('666已经被玩烂了...', 'invalid')

            return value

方式四:同时生成多个标签进行验证

from django.forms import Form

from django.forms import widgets

from django.forms import fields 

from django.core.validators import RegexValidator 

############## 自定义字段 ##############

class PhoneField(fields.MultiValueField):

    def __init__(self, *args, **kwargs):

        # Define one message for all fields.

        error_messages = {

            'incomplete': 'Enter a country calling code and a phone number.',

        }

        # Or define a different message for each field.

        f = (

            fields.CharField(

                error_messages={'incomplete': 'Enter a country calling code.'},

                validators=[

                    RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),

                ],

            ),

            fields.CharField(

                error_messages={'incomplete': 'Enter a phone number.'},

                validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],

            ),

            fields.CharField(

                validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],

                required=False,

            ),

        )

        super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,

                                         **kwargs) 

    def compress(self, data_list):

        """

        当用户验证都通过后,该值返回给用户

        :param data_list:

        :return:

        """

        return data_list

 

############## 自定义插件 ##############

class SplitPhoneWidget(widgets.MultiWidget):

    def __init__(self):

        ws = (

            widgets.TextInput(),

            widgets.TextInput(),

            widgets.TextInput(),

        )

        super(SplitPhoneWidget, self).__init__(ws)

 

    def decompress(self, value):

        """

        处理初始值,当初始值initial不是列表时,调用该方法

        :param value:

        :return:

        """

        if value:

            return value.split(',')

        return [None, None, None]

初始化数据

在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。

1、Form

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator  

class MyForm(Form):

    user = fields.CharField() 

    city = fields.ChoiceField(

        choices=((1, '上海'), (2, '北京'),),

        widget=widgets.Select

    )

2、Views

from django.shortcuts import render, redirect

from .forms import MyForm

 

def index(request):

    if request.method == "GET":

        values = {'user': 'root', 'city': 2}

        obj = MyForm(values) 

        return render(request, 'index.html', {'form': obj})

    elif request.method == "POST":

        return redirect('http://www.google.com')

    else:

        return redirect('http://www.google.com')

3、HTML

<form method="POST" enctype="multipart/form-data">

    {% csrf_token %}

 <p>{{ form.user }} {{ form.user.errors }}</p>

 <p>{{ form.city }} {{ form.city.errors }}</p> 

 <input type="submit"/>

</form>

基于form的django图片上传

第一种,直接上传

views

class TestimgForm(forms.Form):

    title = fields.CharField()

    img = fields.FileField()

def testimg(request):

 import os,time

 if request.method == 'GET':

        img = TestimgForm()

 return render(request, 'backend/testimg.html',{'img':img})

 else:

        obj = TestimgForm(request.POST, request.FILES)

 if obj.is_valid():

	    img = request.FILES.get('img')

            time = time.strftime('%Y%m%d%H%M%S')

             ext = os.path.splitext(img.name)[1]

	     file_path = os.path.join('upload/article',time+ext)

 #将图片重命名

             f = open(file_path,'wb')

 for line in imgs.chunks():

             f.write(line)

             f.close()

 return HttpResponse('上传成功')

第二种,写入数据库

html

<!DOCTYPE html>

<html lang="en">

<head>

    <title>Title</title>

</head>

<body>

<form action="testimg" method="post" enctype="multipart/form-data">

    {% csrf_token %}

    {{ img }}

    <input type="submit" value="提交"/>

</form>

</body>

</html>

views

class TestimgForm(forms.Form):

    title = fields.CharField()

    img = fields.FileField()

def testimg(request):

 import os,time

 if request.method == 'GET':

        img = TestimgForm()

 return render(request, 'backend/testimg.html',{'img':img})

 else:

        obj = TestimgForm(request.POST, request.FILES)

 if obj.is_valid():

		title = obj.cleaned_data['title']

            img = obj.cleaned_data['img']

        time = time.strftime('%Y%m%d%H%M%S')

        ext = os.path.splitext(img.name)[1]

		img.name = time + ext

            Imgtest.objects.create(title=title,img=img)

 return HttpResponse('上传成功')

models.py

class Imgtest(models.Model):
    title = models.CharField(verbose_name='标题',max_length=32,)
    img = models.ImageField(verbose_name='缩略图',upload_to="./article/",
                            help_text="大小200*200,不超过200k",
                            default="/upload/article/common.jpg")

完整代码

class TestimgForm(forms.Form):

    title = fields.CharField()

    img = fields.FileField()

def testimg(request):

 import os,time

 if request.method == 'GET':

        img = TestimgForm()

 return render(request, 'backend/testimg.html',{'img':img})

 else:

        obj = TestimgForm(request.POST, request.FILES)

 if obj.is_valid():

 #(request.FILES['file'])

 # title = request.FILES.get('title')

 # img = request.FILES.get('img')

            title = obj.cleaned_data['title']

            img = obj.cleaned_data['img']

            time = time.strftime('%Y%m%d%H%M%S')

            ext = os.path.splitext(img.name)[1]

            img.name = time + ext

 # Imgtest.objects.create(title=title,img=img)

 # img是对象(文件大小,文件名称,文件内容。。。)

 # file_path = os.path.join('upload/article',time+ext)

            file_path = os.path.join('upload/article', img.name)

            f = open(file_path,'wb')

 for line in img.chunks():

                f.write(line)

            f.close()

 return HttpResponse('...')

Form定制化

定制错误信息

mail = forms.EmailField(error_messages={'required':u'邮箱不能为空'})

定制错误规则

mobile = forms.CharField(validators=[mobile_validate,],

error_messages={'required':u'手机不能为空'})

from django.shortcuts import render
from django import forms
import re
from django.core.exceptions import ValidationError
def mobile_validate(value):
 mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
 if not mobile_re.match(value):
 raise ValidationError('手机号码格式错误')
 
class UserInfo(forms.Form):
 email = forms.EmailField(error_messages={'required':u'邮箱不能为空'})
 host = forms.CharField(error_messages={'required':u'主机不能为空'})
 port = forms.CharField(error_messages={'required':u'端口不能为空'})
 mobile = forms.CharField(validators=[mobile_validate,],                     #定义错误规则函数
      error_messages={'required':u'手机不能为空'},
      widget=forms.TextInput(attrs={'class':'form-control',   #定义class
 'placeholder':u'请输入手机号'}))
      beizhu = forms.CharField(required=False,    #备注允许为空
      widget=forms.Textarea(attrs={'class':'form-control',       #定义为多行输入框
 'placeholder':u'请输入备注'}))
 user_type_choice = (
   (0,u'普通用户'),
       (1,u'高级用户'),
      )
 user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'}))

保存用户输入内容

obj = AccountForm.LoginForm(request.POST)

Form表单验证以及错误信息

错误信息格式:

普通格式:

from django.shortcuts import render,HttpResponse
from web.forms import account as AccountForm
 
def login(request):
 obj = AccountForm.LoginForm(request.POST)
 if request.method == 'POST':
 if obj.is_valid():
 all_data = obj.clean()
 else:
 #用于Form表单提交
       error = obj.errors
 #print error['username'][0]
 #print error['password'][0]
 return render(request, 'account/login.html',{'obj':obj,'error':error})
 
 return render(request, 'account/login.html',{'obj':obj})

 创建一个simple_tag,使其输 error['username'][0]

{% load xx %}
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title></title>
</head>
<body>
 <form action="/login/" method="post">
 <p>{{ obj.username }}
 {% error_msg error.username %}
 </p>
 <p>{{ obj.password }}
 {% error_msg error.password %}
 </p>
 <input type="submit" value="提交">
 </form>

JSON格式

def login(request):
 obj = AccountForm.LoginForm(request.POST)
 if request.method == 'POST':
 if obj.is_valid():
 all_data = obj.clean()
 else:
 #用于Ajax
error = obj.errors.as_json()
 #print error
return HttpResponse(error)
 return render(request, 'account/login.html',{'obj':obj,'error':error})
 return render(request, 'account/login.html',{'obj':obj})

Form生成select标签

静态select

动态select

当增加数据库数据时

不会更新,除非关闭程序,重新载入才会更新

解决方法:利用面向对象类的静态字段

from django import forms
import json
 
class ImportForm(forms.Form):
 #静态select
HOST_TYPE_LIST = (
 (0,'物理机'),
(1,'虚拟机'),
)
 host_type = forms.IntegerField(
 widget=forms.Select(choices=HOST_TYPE_LIST)
 )
 hostname = forms.CharField()
 
 #动态select
 # admin_dic = ((1,'yangmv'),(1,"bob"),)
 # w_obj = open('db_admin','w')
 # w_obj.write(json.dumps(admin_dic))
 # w_obj.close()
f_obj = open('db_admin','r')
 data = f_obj.read()
 data_tuple = json.loads(data)
 admin = forms.IntegerField(
 widget=forms.Select(choices=data_tuple)
 )
 
 def __init__(self,*args,**kwargs):
 super(ImportForm,self).__init__(*args,**kwargs)
 tmp = open('db_admin')
 data = tmp.read()
 data_tuple = json.loads(data)
 self.fields['admin'].widget.choices = data_tuple

  再次增加数据库数据,直接刷新页面即可读取到新数据

优化后代码

from django import forms
import json
 
class ImportForm(forms.Form):
 admin = forms.IntegerField(widget=forms.Select())
 def __init__(self,*args,**kwargs):
 super(ImportForm,self).__init__(*args,**kwargs)
 tmp = open('db_admin')
 data = tmp.read()
 data_tuple = json.loads(data)
 self.fields['admin'].widget.choices = data_tuple

使用models获取select数据

class Select(models.Model):
 username = models.CharField(max_length=16)
 password = models.CharField(max_length=32)

<body>
 <form action="/home/">
 <p>{{ obj.admin }}</p>
 </form>
</body>

def home(request):
 obj = HomeForm.ImportForm()
 return render(request, 'home/home.html',{'obj':obj})
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

换个网名有点难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值