《Django3.0文档》笔记:Models

Django3.0文档》笔记:Models

模型类

模型可以准确且唯一地描述数据,其包含了存储数据的重要字段。每个模型都是一个Python的类,这些模型类继承自django.db.models.Model,模型类的每个属性都相当于数据库表的一个字段。一般来说,每个模型类映射一张数据库表,每个模型类的实例代表数据库表的一行记录,每个字段映射为数据库表的一列。通过模型类和Django提供的一套数据库抽象API,可以创建、检索、更新和删除对象。

from django.db import models


class Person(models.Model):
    first_name = models.charField(max_length=30)
    last_name = models.CharField(max_length=30)

样例代码定义了一个Person模型类,包含first_namelast_name两个字段。根据该模型类可以创建一个如下的数据库表:

CREATE TABLE myapp_person(
    'id' serial NOT NULL PRIMARY KEY,
    'first_name' varchar(30) NOT NULL,
    'last_name' varchar(30) NOT NULL
);

字段

模型中最重要且唯一必要的是数据库的字段定义。字段再类属性中定义。定义字段名时应小心避免使用与模型API冲突的名称,如cleansavedelete等。

from django.db import models


class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_data = models.DataField()
    num_stars = models.IntegerField()

模型类中每一个字段都应该是某个Field字段的实例,Django利用这些字段类来实现以下功能:

  • 字段类型用以指定数据库数据类型(如:INTEGERVARCHARTEXT)。
  • 在渲染表单字段时默认使用的HTML视图(如:<input type='text'><select>)。
  • 基本的有效性验证功能,用于Django后台和自动生成表单。

每一种字段都需要指定一些特定的参数,例如,CharField(以及它的子类)需要接收一个max_length参数,用于指定数据库存储VARCHAR数据时用的字节数。

一些可选的参数是通用的,可以用于任何字段类型,下面介绍一些经常用到的通用参数:

null

如果设置为True,当该字段为空时,Django会将数据库中该字段设置为NULL,默认为False

blank

如果设置为True,则该字段允许为空,默认为False。(注意:该选项与null不同,null选项仅仅是数据库层面的设置,然而blank是涉及表单验证方面。如果一个字段设置为blank=True,在进行表单验证时,接收的数据该字段值允许为空,而设置为blank=False时,不允许为空。

choices

一系列二元组,用作此字段的选项。如果提供了二元组,默认表单小部件是一个选择框,而不是标准文本字段,并将限制给出的选项。一个选项列表:

YEAR_IN_SCHOOL_CHOICES = [
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
]

每个二元组的第一个值会存储在数据库中,而第二个值将只会用于在表单中显示。对于一个模型类的实例,要获取该字段二元组中相对应的第二个值,使用get_Foo_display()方法。例如:

from django.db import models


class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'

也可以使用枚举类以简洁的方式来定义choices:

from django.db import models


class Runner(models.Model):
    MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
    name = models.CharField(max_length=60)
    medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
default

该字段的默认值,可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。

primary_key

如果设置为True,将该字段设置为该模式的主键。在一个模型类中,如果没有对任何一个字段设置primary_key=True选项,Django会自动添加一个IntegerField字段,并设置为主键。因此可以不手动设置主键,也可以手动重写Django默认设置的主键。

主键字段是只读的,如果想修改一个模型类实例的主键并保存,等同于创建一个新的模型类实例。例如:

from django.db import models


class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>
unique

如果设置为True,这个字段的值必须在整个表中保持唯一。

verbose_name

除了ForeignKeyManyToManyFieldOneToOneField,任何字段类型都接收一个可选的位置参数verbose_name,如果未指定该参数值,Django会自动使用字段的属性名作为该参数值,并且把下划线转换为空格。

在该例中,备注名为person's first name:

first_name = models.CharField("person's first name", max_length=30)

在该例中,备注名为first name:

first_name = models.CharField(max_length=30)

ForeignKeyManyToManyFieldOneToOneField接收的第一个参数为模型的类名,后面可以添加一个verbose_name参数:

poll = models.ForeignKey(
    poll,
    on_delete=models.CASCADE,
    verbose_name = "related place",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)

惯例是不将verbose_name的首字母大写,必要时Django会自动把首字母转换为大写。

关联

显然,关系型数据库的强大之处在于各表之间的关联关系。Django提供了定义三种最常见的数据库关联关系的方法:多对一,多对多,一对一。

多对一关联

定义一个多对一的关联关系,使用django.db.models.ForeignKey类,和其他Field字段类型一样,只需要在模型类中添加一个值为关联模型类的属性。

ForeignKey类需要添加一个位置参数,即想要关联的模型类名。

例如,一个Car模型类有一个制造者Manufacturer,一个Manufacturer制造许多辆车,但是每辆车都仅有一个制造者,那么使用下面的方法定义这个关系:

from django.db import models


class Manufacturer(models.Model):
    # ...
    pass


class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

建议设置ForeignKey字段名为想要关联的模型类名,也可以自定义关联字段名,例如:

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
多对多关联

定义一个多对多的关联关系,使用django.db.models.ForeignKey类,和其他Field字段类型一样,只需要在模型类中添加一个值为关联模型类的属性。

ManyToManyField类需要添加一个位置参数,即想要关联的模型类名。

例如,如果Pizza含有多种Topping(配料),一种Topping可能存在于多个Pizza中,并且每个Pizza含有多种Topping,那么可以表示这种关系:

from django.db import models


class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

建议设置ManyToManyField字段名为一个复数名词(如toppings),表示所要关联的模型类对象的集合。

注意:对于多对多关联关系的两个模型类,可以在任何一个模型类中添加ManyToManyField字段,但只能选择一个模型类设置该字段,即不能同时在两模型类中添加该字段。一般情况下,应该把ManyToManyField实例放到需要在表单中被编辑的对象中。在之前的例子中,toppings被放在Pizza当中(而不是Topping中有指向PizzaManyToManyField实例),因为相较于配料被放在不同的披萨当中,披萨当中有很多种配料更加符合常理。按照先前说的,在编辑Pizza的表单时用户可以选择多种配料。

中介模型

如果只是想要一个类似于记录披萨和配料关系之间混合和搭配的多对多关系,标准的ManyToManyField就够用了,但是有时可能需要将数据与两个模型之间的关系相关联。

举例来讲,考虑一个需要跟踪音乐人属于哪个音乐组的应用程序,在人和他们所在的组之间有一个多对多关系,可以使用ManyToManyField来表示这个关系。然而,如果想要记录更多的信息在这样的关联表当中,比如想要记录某人是何时加入一个组的。对于这些情况,Django允许指定用于控制多对多关系的模型类:可以在中介模型当中添加额外的字段。在实例化ManyToManyField的时候使用through参数指定多对多关系使用哪个中介模型。举例代码如下:

from django.db import models


class Person(models.Model):
    name = models.CharField(max_length=128)
    
    def __str__(self):
        return self.name

    
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')
    
    def __str__(self):
        return self.name

    
class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

通过中介模型完成ManyToManyField后,可以通过实例化中介模型来创建多对多关系:

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles, date_joined=date(1962, 8, 16), invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles, date_joined=date(1960, 8, 1), invite_reason="Wanted to from a band.")
>>> beatles.member.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

如果自定义中介模型没有强制(model1, model2)对的唯一性,调用remove()方法会将相关的实例都删除,调用clear()方法则会删除所有的中介模型实例。

>>> Membership.objects.create(person=ringo, group=beatles, date_joined=date(1968, 9, 4), invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

一旦建立了自定义多对多关联关系,就可以执行查询操作,和一般的多对多关联关系一样,可以使用多对多关联模型的属性来查询:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

# Find all the members of The Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(group__name='The Beatles', membership__date__joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
一对一关联

使用OneToOneField来定义一对一关系,就像使用其他类型的Field一样:在模型属性中包含它。当一个对象以某种方式“继承”另一个对象时,这对该对象的主键非常有用。OneToOneField需要一个位置参数:与模型相关联的类。

例如,当建立一个有关“位置”信息的数据库时,可能会包含通常的地址,电话等字段。接着,如果想要接着建立一个关于“餐厅”的数据库,除了将位置数据库当中的字段复制到Restaurant模型类,也可以将一个指向Place模型类的OneToOneField放到Restaurant当中(因为“餐厅”是一个“地点”),事实上,在处理这样的情况下最好使用模型继承,它隐含的包括了一个一对一关系。和ForeignKey一样,可以创建自关联关系也可以创建与尚未定义的模型的关系。

OneToOneField字段还可以接收一个可选的parent_link参数。OneToOneField类通常自动的成为模型的主键,这条规则现在不再使用了(然而可以通过手动指定primary_key参数)。因此,可以在单个模型当中指定多个OneToOneField字段。

字段命名限制

Django对模型类的字段名有一些限制:

1.一个字段的名称不能是Python保留字,因为这会导致Python语法错误。比如:

class Example(models.Model):
    pass = models.IntegerField() # 'pass' is a reserved word!

2.一个字段名称不能包含连续的多个下划线,原因在于Django查询语法的工作方式。比如:

class Example(models.Model):
    foo__bar = models.IntegerField() # 'foo__bar has two underscores!'

3.字段名不能以下划线结尾,原因同上。

SQL保留字,例如joinwhereselect,是可以被用在模型字段名当中的,因为Django在对底层的SQL查询当中清洗了所有的数据库表名和字段名,通过使用特定数据库引擎的引用语法。

Meta选项

使用内部Meta类来给模型赋予元数据,就像:

from django.db import models


class 0x(models.Model):
    horn_length = models.IntegerField()
    
    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

模型的元数据即“所有不是字段的东西”,比如排序选项(oedering),数据库表名(db_table),或是阅读友好的单复数名(verbose_nameverbose_name_plural)。这些都不是必须的,并且在模型当中添加Meta类也是完全可选的。

模型属性

objects

模型当中最重要的属性是Manager,它默认是Django模型和数据库查询操作之间的接口,并且它被用作从数据库中获取实例对象,如果没有指定自定义的Manager,默认名称是objectsManager只能通过模型类来访问,不能通过模型实例来访问。

模型方法

在模型方法中添加自定义方法会给你的对象提供自定义的“行级”操作能力,与之对应的是类Manager的方法意在提供“表级”的操作,模型方法应该在某个对象实例上生效。这是一个将相关逻辑代码放在一个地方的技巧——模型。比如,该模型有一些自定义方法:

from django.db import models


class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()
    
    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby-boomer"
        else:
            return "Post-boomer"
        
	@property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    

例子中的最后一个方法是property,下面介绍两个最可能期望复写的:

__str__()

一个Python的“魔法方法”,返回值友好地展示了一个对象。PythonDjango在要将模型实例展示为纯文本时调用。最有可能的应用场景是交互式控制台或后台。

get_absolute_url()

该方法告诉Django如何计算一个对象的URLDjango在后台接口使用此方法,或任意时间它需要计算一个对象的URL。任何需要一个唯一URL的对象需要定义此方法。

模型继承

模型继承在Django中与普通类继承在Python中的工作方式几乎完全相同,但也应该遵循本页开头的内容。这意味着其基类应该继承自django.db.models.Model

你只需要决定父类模型是否需要拥有它们的权利(拥有它们的数据表),或者父类仅作为承载仅子类中可见的公共信息的载体。

Django有三种可用的继承风格。

  • 常见情况下,仅将父类用于子类公共信息的载体,因为你不会想在每个子类中把这些代码都敲代一遍。这样的父类永远都不会单独使用,所以抽象基类是你需要的。
  • 若继承了一个模型(可能来源于其它应用),且想要每个模型都有对应的数据表,请使用多表继承
  • 最后,若只想修改模型的Python级行为,而不是以任何形式修改模型字段,代理模型是最佳选择。
抽象基类

抽象基类在你要将公共信息放入很多模型时会很有用。编写你的基类,并在Meta类中填入abstract=True。该模型将不会创建任何数据表。当其用作其他模型类的基类时,它的字段会自动添加至子类。举个例子:

from django.db import models


class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()
    
    class Meta:
        abstract = True

class Students(CommonInfo):
    home_group = models.CharField(max_length=5)

Student模型拥有3个字段:nameagehome_groupCommonInfo模型不能用作普通的Django模型,因为它是一个抽象基类。它不会生成数据表,也没有管理器,也不能被实例化和保存。从抽象基类继承来的字段可以被其他字段或值重写,或用None删除。对很多用户来说,这种继承可能就是你想要的,它提供了一种在Python级抽出公共信息的方法,但仍会在子类模型中创建数据表。

Meta继承

当一个抽象基类被建立,Django将所有你在基类中申明的Meta内部类以属性的形式提供。若子类未定义自己的Meta类,他将会继承父类的Meta类,当然,子类也可直接继承父类的Meta,比如:

from django.db import models


class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

Django在安装Meta属性前,对抽象基类的Meta做了一个调整——设置abstract=False。这意味着抽象基类的子类不会自动地变成抽象类。当然,你可以继承一个抽象基类创建另一个抽象基类,只需记住显式地设置abstract=True。抽象基类的某些Meta属性对子类是没用的,比如包含db_table意味着所有的子类(你并未在子类中指定他们的Meta)会使用同一张数据表,这肯定不是我们想要的。

related_namerelated_query_name

若你在外键或者多对多字段使用了related_namerelated_query_name,你必须为该字段提供一个独一无二的反向名字和查询名字。这在抽象基类中一般会引发问题,因为基类中的字段都被子类继承且保持同样的值(包括related_namerelated_query_name)。为了解决此问题,当你在抽象基类中(也只能是在抽象基类中)使用了related_namerelated_query_name,部分值需要包含'%(app_lable)s''%(class)s'

  • '%(class)s' 用使用了该字段的子类的小写类名替换。
  • '%(app_label)s' 用小写的包含子类的应用名替换。每个安装的应用名必须是唯一的,应用内的每个模型类名也必须是唯一的。因此,替换后的名字也是唯一的。、

举个例子,有个应有common/models.py

from django.db import models


class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )
    
    class Meta:
        abstract = True

class ChildA(Base):
    pass

class ChildB(Base):
    pass

附带另一个应用rare/models.py

from common.models import Base


class ChildB(Base):
    pass

common.ChildA.m2m 字段的反转名是 common_childa_related,反转查询名是common_childascommon.ChildB.m2m 字段的反转名是common_childb_related, 反转查询名是 common_childbsrare.ChildB.m2m 字段的反转名是rare_childb_related,反转查询名是 rare_childbs。这决定于你如何使用 '%(class)s''%(app_label)s' 构建关联名字和关联查询名。但是,若你忘了使用它们,Django 会在你执行系统检查(或运行 migrate)时抛出错误。

如果你未指定抽象基类中的related_name) 属性,默认的反转名会是子类名,后接 '_set' 。这名字看起来就像你在子类中定义的一样。比如,在上述代码中,若省略了 related_name) 属性, ChildAm2m 字段的反转名会是 childa_setChildB 的是 childb_set

多表继承

Django支持的第二种模型继承方式是层次结构中的每个模型都是一个单独的模型。某个模型都指向分离的数据表,且可被独立查询和创建。继承关系介绍了子类和父类之间的连接(通过一个自动创建的 OneToOneField )。比如:

from django.db import models


class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)
    
class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=True)

Place的所有字段均在Restaurant中可用,虽然数据分别存在不同的表中,所以,以下操作均可:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

若有一个Place同时也是Restaurant,你可以通过小写的模型名将Place对象转为Restaurant对象。

>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>

然而,若上述例子中的 p 不是 一个 Restaurant (它仅是个 Place 对象或是其它类的父类),指向 p.restaurant 会抛出一个 Restaurant.DoesNotExist 异常。

Restaurant 中自动创建的连接至 PlaceOneToOneField看起来像这样:

place_ptr = models.OneToOneField(
    Place, on_delete=models.CASCADE,
    parent_link=True,
    primary_key=True,
)

你可以在 Restaurant 中重写该字段,通过申明你自己的 OneToOneField,并设置 parent_link=True

Meta 和多表继承

多表继承情况下,子类不会继承父类的 Meta。所以的 Meta 类选项已被应用至父类,在子类中再次应用会导致行为冲突(与抽象基类中应用场景对比,这种情况下,基类并不存在)。故,子类模型无法访问父类的 Meta类。不过,有限的几种情况下:若子类未指定 ordering 属性或 get_latest_by 属性,子类会从父类继承这些。

如果父类有排序,而你并不期望子类有排序,你可以显示的禁止它:

class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []
继承与反向关系

由于多表继承使用隐式的 OneToOneField连接子类和父类,所以直接从父类访问子类是可能的,就像上述例子展示的那样。然而,使用的名字是 ForeignKeyManyToManyField关系的默认值。如果你在继承父类模型的子类中添加了这些关联,你必须指定 related_name属性。假如你忘了,Django 会抛出一个合法性错误。

比如,让我们用上面的 Place 类创建另一个子类,包含一个 ManyToManyField:

class Supplier(Place):
    customers = models.ManyToManyField(Place)

这会导致以下错误:

Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.

related_name 像下面这样加至 customers 字段能解决此错误: models.ManyToManyField(Place, related_name='provider')

代理模型

使用多表继承时,每个子类模型都会创建一张新表。这一般是期望的行为,因为子类需要一个地方存储基类中不存在的额外数据字段。不过,有时候你只想修改模型的Python级行为——可能是修改默认管理器,或添加一个方法。

这是代理模型继承的目的:为原模型创建一个代理。你可以创建、删除和更新代理模型的实例,所有的数据都会存储的像你使用原模型(未代理的)一样。不同点是你可以修改代理默认的模型排序和默认管理器,而不需要修改原模型。

代理模型就像普通模型一样申明。你需要告诉Django这是一个代理模型,通过将Meta类的proxy属性设置为True

例如,假设你想为Person模型添加一个方法,你可以这么做:

from django.db import models


class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    
class MyPerson(Person):
    class Meta:
        proxy = True
    
    def do_something(self):
        # ...
        pass

MyPerson 类与父类 Person 操作同一张数据表。特别提醒, Person 的实例能通过 MyPerson 访问,反之亦然。

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

你也可以用代理模型定义模型的另一种不同的默认排序方法。你也许不期望总对 Persion 进行排序,但是在使用代理时,总是依据 'last_name' 属性进行排序:

class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

现在,普通的 Person 查询结果不会被排序,但 OrderdPerson 查询会按 last_name 排序。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值