乐虎游戏|乐虎国际登录|欢迎你

《Thinkphp5入门体系课程》第十三课:Migration(二)

日期:2020-01-30编辑作者:计算机资讯

Thinkphp5视频教程

cmd操作mysql方式:

 

  • 表的方法
  • 列的方法
  • 自定义主键名
  • 自定义时间戳字段名
  • 增加软删除 softDelete 字段

cmd登录数据库方法:(方法一二实用于在电脑里配置过数据库相关的环境变量,否则必须在cmd里进入到数据库的bin目录):

 

模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和操作行为。通常,每个模型都对应着数据库中的唯一一张表。

基础认识:

  • 每个model都是一个继承django.db.models.Model的子类;
  • model中的每个属性(attribute)都代表数据库中的一个字段;
  • Django 提供一套自动生成的用于数据库访问的API;详见Making queries。

上一篇文章中,作者给大家简单的介绍了 Migration 的使用,但是较为浅薄不够深入,很多小伙伴看完肯定意犹未尽,这篇文章就给小伙伴们深入的介绍 Migration 的使用咯。

  1).mysql -u root -p

简短例子

例子中的模型定义了一个带有first_namelast_namePersonmodel:

from django.db import models
class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

其中first_namelast_name都是模型的字段 ,每个字段(fields)都是类的一个属性,每个属性映射到数据库中的列。

上面的Personmodel将会像下面sql语句类似的创建一张表:

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

特别注意:

  • 表的名字myapp_person_name是根据模型中的元数据自动生成的(app_name + class_name),也可以自定义表的名称,参考这里Table names;
  • id字段是自动添加的,你可以重新改写这一行为,参见自增主键;
  • 本示例中的CREATETABLESQL使用的是PostgreSQL语法,但是在应用中,Django会根据settings 文件中指定的数据库类型来使用相应的SQL语句。

看过上一篇文章中我们都知道,在 Migration 文件中,我们通过:

  显示Enter password输入你登录数据库时的密码。

使用模型

定义好模型之后,接下来你需要告诉Django使用这些模型,你要做的就是修改配置文件中的INSTALLED_APPS设置, 在其中添加models.py所在应用的名称。

例如,如果你的应用的模型位于myapp.models(文件结构是由manage.pystartapp命令自动创建的),INSTALLED_APPS部分看上去应该是:

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

当你在INSTALLED_APPS中添加新的应用名时,一定要运行命令manage.pymigrate, 你可以事先使用manage.pymakemigrations给应用生成迁移脚本。

$userTable = $this->table;

修改数据库密码

字段

对于一个模型来说,最重要的是列出该模型在数据库中定义的字段。字段由models类属性指定。 要注意选择的字段名称不要和models API冲突,比如cleansave或者delete等。

例子:

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_date = models.DateField()
    num_stars = models.IntegerField()

获取了表 users 的实例,获取这个实例之后我们就可以做很多关于表的操作咯,比如说下面这样:

  旧密码:A 新密码 B:

字段类型

模型中的每个字段都是Field子类的某个实例,Django根据字段类的类型确定以下信息:

  • 数据库中字段的类型(e.g.INTEGER,VARCHAR,TEXT);
  • 渲染表单时使用的默认HTMLwidget(e.g.<inputtype="text">,<select>);
  • 在Django的admin和自动生成的表单中使用的最低验证要求;

Django有几十种内置字段类型;你可以在模型字段参考找到所有的字段, 如果Django内置字段类型无法满足要求,你也可以自定义字段,参见Writing custom model fields。

$table->addColumn('nickname', 'string', ['limit' => 16, 'null' => false]) ->addColumn('email', 'string', ['limit' => 32, 'null' => false]) ->addColumn('password', 'string', ['limit' => 64, 'null' => false]) ->setEngine ->comment; ->create();

  mysqladmin -uroot -pA password 'B'

字段选项

每个字段都有一些特有的参数,详见模型字段参考。 例如,max_length参数来指定数据库字段VARCHAR的大小。

下面是所有字段类型的通用选项,它们都是可选的,reference有它们详细的介绍。下面只对最常用的一种选项快速总结:

null

如果为True,Django将会把数据库中的空值保存为NULL。默认值为False.

blank

如果为True,该字段允许为空值,默认为False.

要注意,blanknull不同。null纯粹是数据库范畴,指数据库中字段内容是否允许为空, 而blank是表单数据输入验证范畴的。如果一个字段的blank=True, 表单的验证将允许输入一个空值。如果字段的blank=False,该字段就是必填的。

choices

由二项元组构成的一个可迭代对象(列表或元组),用来给字段提供选择项。 如果设置了choices,默认的表单将是一个选择框而不是文本框,而且这个选择框的选项就是choices中的选项。 下面是一个关于choices列表的例子:

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

每个元组中的第一个元素是存储在数据库中的值。第二个元素是在管理界面或ModelChoiceField中用作显示的内容。给定一个模型实例, 可以使用get_FOO_display()方法获取一个选择字段的显示值(这里的FOO就是choices字段的名称 )。例如:

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'

default

字段的默认值。这可以是一个值,也可以是可调用对象。如果可调用,那么每次创建新对象时都会被调用。

help_text

表单部件额外显示的帮助内容。即使字段不在表单中使用,它对生成文档也很有用。

primary_key

如果为True,那么这个字段就是模型的主键。

如果你不在的模型中指定任何一个字段primary_key=True, 那么Django将自动添加一个IntegerField来作为主键, 所以你不需要在任何一个字段中设置primary_key=True,除非你想重写默认的主键行为。

主键字段是只读的。如果你在一个已存在的对象上面更改主键的值并且保存,那么其实是创建了一个新的对象。例如:

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)
['Apple', 'Pear']

unique

如果该值设置为True, 这个数据字段在整张表中必须是唯一的。

最后重申,这些只是对最常见的字段选项的简短描述,更加详细内容参见公共字段选项。

给表 users 增加三个字段,并指定使用 InnoDB 存储引擎,且给表打上 用户表 的注释。这是一个简单的实例,关于表的操作方法主要有下面这些方法:

刷新权限:

自增主键

默认情况下Django会给所有的model添加类似下面的字段:

id = models.AutoField(primary_key=True)

这就是自增主键。

如果你想将某个字段设置为主键,在那个字段设置primary_key=True即可, Django发现已经有字段设置了primary_key=True时, 将不会再自动添加id字段。

每个model都必须要有一个字段是primary_key=True。 如果你不添加,Django将自动添加。

方法 说明
setId(string $id) 设置主键字段名
setPrimaryKey(string $key) 设置主键
setEngine(string $engine) 设置存储引擎,有:InnoDB,MyISAM
setComment(string $comment) 设置表注释
addTimestamps(string $createAtName, string $updateAtName) 给表加上创建时间编辑时间两个字段,默认字段名是:create_time,update_time
addColumn($columnName, $type, $options) 给表增加一个字段
changeColumn($columnName, $newType, $options) 改变表的某一个字段的属性
create() 创建表
save() 保存表
rename($newTableName) 重命名表名
hasTable($tableName) | exists() 判断表是否存在
drop() 删除当前表
setIndexes(array $indexs) 批量设置索引
setForeignKeys(array $foreignKeys) 设置外键
removeColumn($columnName) 删除字段
renameColumn($oldName, $newName) 字段重命名
insert(array $data) 插入数据

  flush priviliges;

字段描述名

ForeignKeyManyToManyFieldOneToOneField外,每个字段类型都接受一个可选的位置参数(第一个位置)——字段的描述名。 如果没有给定描述名,Django将根据字段的属性名称自动创建描述名——将属性名称的下划线替换成空格。

比如这个例子中描述名是person'sfirstname:

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

而没有主动设置时,则是firstname:

first_name = models.CharField(max_length=30)

ForeignKey,ManyToManyFieldOneToOneField也是有描述名的,只是他们的第一个参数必须是模型类, 所以描述名需要通过关键字参数verbose_name传入:

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

习惯上verbose_name不用大写首字母,在必要的时候Django会自动大写首字母。

列我们主要从 addColumn($columnName, $type, $options) 这个方法的参数讲起,第一个参数:

查看数据库(表)的字符集编码:

关系

显然,关系数据库的特点在于相互关联的表。Django提供了定义三种最常见的数据库关系类型方法:多对一、多对多和一对一。

参数一:列名

其实这里叫列名并不准确,但是我们姑且叫做列名,本着入门的原则,这里不过多的深入。

它的结构非常简单,字符串类型,符合Mysql的列名规范就可以啦,其它没有要说的。

  show create(表) database 数据库名;

多对一

Django使用django.db.models.ForeignKey来定义一个多对一的关系,和Field类型一样, 在模型当中把它做为一个类属性包含进来。django.db.models.ForeignKey接收一个位置参数——与model关联的类。

例如:一辆汽车(Car)只有一家制造商(Manufacturer),但是一家制造商可以生产很多辆汽车,所以可以按照这样方式来定义:

from django.db import models
class Manufacturer(models.Model):
    # ...
    pass
class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

你还可以创建递归关联关系(对象和自己进行多对一关联)和与尚未定义的模型的关联关系;详见。模型字段参考

建议你用被关联的模型的小写名称做为ForeignKey字段的名字 (例如,上面manufacturer)。当然,你也可以起别的名字。例如:

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

参见

模型字段参考中有详细的ForeignKey字段的其他参数。

这些选项帮助定义关联关系应该如何工作;它们都是可选的参数。 访问反向关联对象的细节,请见反向关联示例.

示例代码,请见多对一关系模型示例.

参数二:列类型

到目前为止,可以支持下面这些类型:

类型
biginteger
binary
boolean
date
datetime
decimal
float
integer
string
text
time
timestamp
uuid

当MysqlVersion >= 5.7的时候,还有下面的类型:

类型
enum
set
blob
json

好咯,类型就说到这里了,自己对号入座。

临时修改数据库的字符集:

多对多

使用ManyToManyField定义多对多关系。 用法和其他Field字段类型一样:在模型中做为一个类属性包含进来。

ManyToManyField需要传入一个位置参数: 与之关联的模型。

例如,一个披萨可以有多种馅料,一种馅料也可以位于多个披萨上。 如下展示:

from django.db import models
class Topping(models.Model):
    # ...
    pass
class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

ForeignKey一样, 你同样可以创建递归关联关系(对象与自己的多对多关联) 和与尚未定义的模型的关联关系.

建议你以被关联模型名称的复数形式做为ManyToManyField的名字(例如,上面的toppings)。

ManyToManyField设置到哪个模型中并不重要,但是你只能在两个模型中的一个设置,不能两个都设置。

通常,ManyToManyField实例应该位于可以编辑的表单中。在上面的例子中,toppings位于Pizza中(而不是在Topping里面设置pizzasManyToManyField字段), 因为设想一个Pizza有多种Topping比一个Topping位于多个Pizza上要更加自然。 按照上面的方式,在Pizza的表单中将允许用户选择不同的Toppings

参见

完整的示例参见多对多关系模型示例。

ManyToManyField还接受其他参数,你可以在模型字段参考中查看。 这些选项帮助定义关系应该如何工作;它们都是可选的

参数三:可选参数

下面是所有字段类型均支持的可选参数:

参数 说明
limit 长度限制,整数
length limit,整数
default 默认值,mixed
null 是否可空,bool
after 在哪个字段后
comment 注释

下面是针对 decimal 类型:

参数 说明
precision 长度,整数
scale 小数位长度,整数
signed 是否无符号,bool

下面是针对 enumset 类型:

参数 说明
values 默认值

下面是针对 integerbiginteger 类型:

参数 说明
identity 自动递增,bool,默认false
signed 无符号,bool

下面是针对 timestamp 类型:

参数 说明
default 默认值,如:CURRENT_TIMESTAMP
update 字段更新时的动作,如:CURRENT_TIMESTAMP

好咯,列就说到这里啦。

看过上一篇的小伙伴们都知道,默认情况系统会给我们自动加上 id 字段的主键,但是如果我们想使用 uid 怎么办呢?可以这样:

$table = $this->table;$table->setId;

这样就可以啦。

执行 addTimestamps() 方法系统会为我们自动加上 create_timeupdate_time 两个字段,但是如果是从 laravel 转过来的用户,肯定不习惯这个命名咯,想使用 created_atupdated_at 是最好不过啦,于是可以这样:

$table = $this->table;$table->addTimestamps('created_at', 'updated_at');

注意,虽然这里修改了,但是你还需要修改下模型的文件,因为系统默认是 create_timeupdate_time ,比如说我们有个 User 模型,可以这样:

<?phpnamespace appcommonmodel;use thinkModel;class User extends Model{ // 创建时间字段 protected $createTime = 'created_at'; // 更新时间字段 protected $updateTime = 'updated_at';}

项目中有一些表的数据是不能直接删除的,只是修改成了不显示的状态,而 softDelete 的方案就很好的解决这个问题不仅可以标记记录的删除状态还记住了记录删除的时候,所以如果想要添加 softDelete 可以这样:

$table = $this->table;$table->addSoftDelete();

系统为我们提供的方法并不是很友好,因为它的字段名默认就是 delete_time 而且无法修改,所以这种方法并不推荐,可以手动的添加:

$table = $this->table;$table->addColumn('delete_time', 'timestamp', ['null' => true]);

好了今天的教程就到这里啦。此篇是小滕的《Thinkphp5入门系列课程》第十二课:Migration。喜欢的给个订阅呗!由于作者水平有限,如有错误请欢迎指正。

  SET GLOBAL character_set_server=utf8;

多对多关系中的其他字段

处理类似搭配pizzatopping这样简单的多对多关系时,使用标准的ManyToManyField就可以了。 但是,有时你可能需要关联数据到两个模型之间的关系上。

例如,有这样一个应用,它记录音乐家所属的音乐小组。我们可以用一个ManyToManyField表示小组和成员之间的多对多关系。但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。

对于这些情况,Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。 源模型的ManyToManyField字段将使用through参数指向中介模型。 对于上面的音乐小组的例子,代码如下:

from django.db import models
class Person(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):              # __unicode__ on Python 2
        return self.name
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')
    def __str__(self):              # __unicode__ on Python 2
        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)

在设置中介模型时,要显式地指定外键并关联到多对多关系涉及的模型。这个显式声明定义两个模型之间是如何关联的。

但是中介模型还一些限制:

  • 中介模型必须有且只有一个外键到源模型(上面例子中的Group), 或者你必须使用ManyToManyField.through_fields显式指定Django 应该在关系中使用的外键。

    如果你的模型中存在不止一个外键,并且through_fields没有指定, 将会触发一个无效的错误。 对目标模型的外键有相同的限制(上面例子中的 Person)

  • 对于通过中介模型与自己进行多对多关联的模型,允许存在到同一个模型的两个外键, 但它们将被当做多对多关联中一个关系的两边。如果有超过两个外键, 同样你必须像上面一样指定through_fields,否则将引发一个验证错误。

  • 使用中介模型定义与自身的多对多关系时,你必须设置symmetrical=False详见模型字段参考。

既然你已经设置好ManyToManyField来使用中介模型(在这个例子中就是Membership), 接下来你要开始创建多对多关系。你要做的就是创建中介模型的实例:

>>> 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 form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

与普通的多对多字段不同,你不能使用add()create()set()语句来创建关系:

>>> # The following statements will not work
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])

为什么不能这样做? 这是因为你不能只创建PersonGroup之间的关联关系, 你还要指定Membership模型中所需要的所有信息;而简单的addcreate和赋值语句是做不到这一点的。 所以它们不能在使用中介模型的多对多关系中使用。此时,唯一的办法就是创建中介模型的实例。

remove()方法被禁用也是出于同样的原因。

如果中间模型定义的自定义表在(model1,model2)的执行不存在唯一性,则remove()调用将不能提供足够的信息来删除中间模型实例:

>>> 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 will not work because it cannot tell which membership to remove
>>> beatles.members.remove(ringo)

但是clear()方法却是可用的。它可以清空某个实例所有的多对多关系:

>>> # 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]>

如果你需要访问一个成员的信息,你可以直接获取Membership模型:

>>> 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.'

另一种获取相同信息的方法是,在Person对象上查询多对多反转关系:

>>> 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来定义一对一关系。 用法和其他字段类型一样:在模型里面做为类属性包含进来。

当某个对象想扩展自另一个对象时,最常用的方式就是在这个对象的主键上添加一对一关系。

OneToOneField接收一个位置参数:与之关联的模型。

例如,你想建一个“places” 数据库,里面有一些常用的字段,比如address、 phone number 等等。 接下来,如果你想在Place 数据库的基础上建立一个Restaurant数据库,而不想将已有的字段复制到Restaurant模型, 那你可以在Restaurant添加一个OneToOneField字段, 这个字段指向Place(因为Restaurant 本身就是一个Place;事实上,在处理这个问题的时候,你应该使用继承,它隐含一个一对一关系)

和使用ForeignKey一样,你可以定义递归的关联关系和引用尚未定义的模型。

参见

在一对一关系模型例子中有完整例子。

OneToOneField字段同时还接收一个可选的parent_link参数。

在以前的版本中,OneToOneField字段会自动变成模型的primary_key。 不过现在已经不这么做了(不过要是你愿意的话,你仍可以传递primary_key参数来创建主键字段)。 所以一个模型中可以有多个OneToOneField字段

  (1).创建数据库:creat database 数据库名;

跨文件的模型

也可以将一个模型与另一个应用程序中的一个模型关联。为此,在定义模型的文件顶部导入相关模型。然后, 只需在需要的地方引用其模型类即可。例如:

from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

     使用数据库:use 数据库名;

字段名称限制

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!
    

这些限制有变通的方法,因为没有要求字段名称必须与数据库的列名匹配。 参见db_column选项。

SQL 的保留字例如joinwhereselect,可以用作模型的字段名, 因为Django 会对底层的SQL 查询语句中的数据库表名和列名进行转义。 它根据你的数据库引擎使用不同的引用语法。

  (2).显示你创建的数据库(已供你选择你要使用的数据库):show databases;

自定义字段类型

如果已有的模型字段都不合适,或者你想用到一些很少见的数据库列类型的优点, 你可以创建你自己的字段类型。创建你自己的字段在Writing custom model fields中有完整讲述。

  (3).删除数据库(谨慎使用此指令):drop database 数据库名;

Meta选项

使用内部的classMeta定义模型的元数据,例如:

from django.db import models
class Ox(models.Model):
    horn_length = models.IntegerField()
    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

模型元数据是“任何不是字段的数据”,比如排序选项 (ordering), 数据库表名 (db_table), 或者 人类可读的单复数名称 (verbose_nameverbose_name_plural)。在模型中添加classMeta是可选的,所有选项都不是必须的。

Meta选项的完整列表可以在模型选项参考中找到。

  (4).展示表的内容:show tables;

模型属性

objects
模型最重要的属性是Manager。它是Django 模型进行数据库查询操作的接口, 并用于从数据库获取实例。 如果没有定义自定义Manager, 则默认名称为objects。Managers只能通过模型类访问,而不是模型实例。

  (5). 重命表名:alter table 原表名 rename to 新表名;

模型方法

通过在模型中添加自定义方法来给对象添加“row级别”的功能。而Manager方法的目的是做“table范围”的事情, 模型方法应该着眼于一个特定的模型实例。

这是一个非常有价值的技术,让业务逻辑位于同一个地方 —— 模型中。

例如,下面的模型具有一些自定义的方法:

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"
    def _get_full_name(self):
        "Returns the person's full name."
        return '%s%s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

这个例子中的最后一个方法是一个property。

在模型实例参考中有完整的为模型自动生成的方法列表。 你可以重写其中的大部分 – 参考重载预定义模型方法, 但是有些方法你会始终想要重新定义:

__str__()(Python 3)

Python的“魔法方法”,它返回任何对象的unicode“表示” 。 这就是Python和Django在模型实例需要被显示和强制转为为普通字符串时将使用的内容。 最明显是在交互式控制台或者管理站点显示一个对象的时候。

你会经常需要重新定义这个方法;默认方法返回的内容几乎没有帮助。

__unicode__()(Python 2)

Python 2 中使用,相当于__str__()

get_absolute_url()

它告诉Django 如何计算一个对象的URL。Django 在它的管理站点中使用到这个方法, 在其它任何需要计算一个对象的URL 时也将用到。

任何具有唯一标识自己的URL 的对象都应该定义这个方法。

  (6).添加字段:alter table 表名 add 字段名 字段类型 字段属性;

重载预定义模型方法

还有另外一部分封装数据库行为的模型方法, 你可能想要自定义它们。特别是,你将要经常改变save()delete()的工作方式。

您可以自由地重写这些方法(以及任何其他模型方法)来改变行为。

重写内置方法的经典用例是,如果您想要在保存对象时做些其他事情。例如,(请参阅save()文档以获得它所接受的参数):

from django.db import models
class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()
    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()

你还可以阻止保存:

from django.db import models
class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()
    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko shall never have her own blog!
        else:
            super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.

必须要记住调用超类的方法——super(Blog,self).save(*args,**kwargs)—— 来确保对象被保存到数据库中。 如果你忘记调用超类的这个方法,默认的行为将不会发生且数据库不会有任何改变。

还要记住传递参数给这个模型方法 —— 即*args,**kwargs。 Django 在未来一直会扩展内建模型方法的功能并添加新的参数。 如果在你的方法定义中使用*args,**kwargs将保证你的代码自动支持这些新的参数。

批量操作中被重载的模型方法不会被调用

注意,当使用查询集批量删除对象或者使用cascadingdelete时,将不会为每个对象调用delete()方法。为确保自定义的删除逻辑得到执行,你可以使用pre_deleteand/orpost_delete信号。

不幸的是,当批量creatingorupdating对象时没有变通方法, 因为不会调用save()pre_save, 和post_save

  (7).修改字段名:alter table 字段名 change 新字段名;

执行自定义SQL

另一种常见的需求是在模型方法和模块级方法中编写自定义SQL语句。有关使用原始SQL的更多细节参见使用原始 SQL。

  (8).删除字段: alter table 表名 drop 字段名;

模型继承

Django 中的模型继承与 Python 中普通类继承方式几乎完全相同, 但是本页开头列出的模型基本的要求还是要遵守。这表示自定义的模型类应该继承django.db.models.Model

你唯一需要作出的决定就是你是想让父模型具有它们自己的数据库表, 还是让父模型只持有一些共同的信息而这些信息只有在子模型中才能看到。

在Django 中有3种风格的继承

  1. 通常,你只想使用父类来持有一些信息,你不想在每个子模型中都敲一遍。 这个类永远不会单独使用,所以你要使用抽象基类。
  2. 如果你继承一个已经存在的模型且想让每个模型具有它自己的数据库表,那么应该使用多表继承。
  3. 最后,如果你只是想改变一个模块级别的行为,而不用修改模型的字段,你可以使用代理模型.

  (9).删除表:drop table 表名;

抽象基类

当你想将一些共有信息放进其它一些model的时候,抽象化类是十分有用的。你编写完基类之后,在Meta中设置abstract=True这个模型就不会被用来创建任何数据表。取而代之的是,当它被用来作为一个其他model的基类,它的字段将被加入那些子类中。 如果抽象基类和它的子类有相同的字段名,那么将会出现error(并且Django将抛出一个exception)。

例如:

from django.db import models
class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()
    class Meta:
        abstract = True
class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

Student将会有三个字段:name,agehome_groupCommonInfo模型无法像一般的Django模型一样使用, 因为它是一个抽象基类。 它无法生成一张数据表或者拥有一个管理器,并且不能实例化或者直接储存。

许多应用场景下, 这种类型的模型继承恰好是你想要的。它提供一种在Python语言层级上提取公共信息的方式, 同时在数据库层级上,每个子类各自仍然只创建一个数据库表。

在navicat下对表的一些操作:

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属性之前, Django 会设置abstract=False。 这意味着抽象基类的子类本身不会自动变成抽象类。 当然,你可以让一个抽象基类继承自另一个抽象基类, 你只要记得每次都要显式地设置abstract=True

一些属性被包含在抽象基类的Meta里面是没有意义的。例如,包含db_table属性将意味着所有的子类(是指那些没有指定自己的Meta类的子类)都使用同一张数据表, 这总归不会是你想要的。

  show create table 表名(可以以代码格式查看你的表);

小心使用related_namerelated_query_name

如果你在ForeignKeyManyToManyField字段是使用related_namerelated_query_name属性, 您必须始终为该字段指定惟一的反向名称和查询名称。 但在抽象基类上这样做就会引发一个很严重的问题, 应为Django会将基类字段添加到每个子类中,因此每个子类都具有相同的属性值(包括related_name和related_query_name)。

当您在抽象基类中使用related_namerelated_query_name) 时,要解决这个问题,名称中就要包含“%(app_label)s”“%(class)s”

  • '%(class)s'会替换为子类的小写加下划线格式的名称,字段在子类中使用。
  • '%(app_label)s'会替换为应用的小写加下划线格式的名称,应用包含子类。每个安装的应用名称都应该是唯一的,而且应用里每个模型类的名称也应该是唯一的,所以产生的名称应该彼此不同。

例如,假设有一个app叫做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_childbs。最后,rare.ChildB.m2m字段的反向名称是rare_childb_related,反向查询名称是rare_childbs。这取决于你如何使用'%(class)s''%(app_label)s'来构造。 如果你没有这样做,Django 就会在验证 model (或运行migrate) 时抛出错误。

如果你没有在抽象基类中为某个关联字段定义related_name属性, 那么默认的反向名称就是子类名称加上'_set', 它能否正常工作取决于你是否在子类中定义了同名字段。 例如,在上面的代码中,如果去掉related_name属性,ChildA中,m2m字段的反向名称是childa_set;而ChildB中的m2m字段的反向名称就是childb_set

related_query_name可以添加'%(app_label)s''%(class)s'是在1.10中加入。

  在设置time 的时候 想让它默认当前时间,和每个表必须有一个主键,如果你没有什么能设置主键的字段,就用auto_increment自增列为主键。该字段属性也要添加primary key

多表继承

这是 Django 支持的第二种继承方式。使用这种继承方式时, 每一个层级下的每个 model 都是一个真正意义上完整的 model 。 每个 model 都有专属的数据表, 都可以查询和创建数据表。 继承关系在子 model 和它的每个父类之间都添加一个链接 (通过一个自动创建的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=False)

Place里面的所有字段在Restaurant中也是有效的, 只不过没有保存在数据库中的Restaurant表中。 所以下面两个语句都是可以运行的:

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

如果你有一个Place它同时也是一个Restaurant, 那么你可以使用 model 的小写形式从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异常。

建表:

多表继承的Meta

在多表继承中,子类继承父类的Meta类是没什么意义的。 所有的Meta选项已经对父类起了作用,再次使用只会起反作用。 (这与使用抽象基类的情况正好相反,因为抽象基类并没有属于它自己的内容)

所以子 model 并不能访问它父类的Meta类。但是在某些受限的情况下,子类可以从父类继承某些行为:如果子类没有指定ordering属性或get_latest_by属性,它就会从父类中继承这些属性。

如果父类有了排序设置,而你并不想让子类有任何排序设置,你就可以显式地禁用排序:

class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []

本文由乐虎游戏发布于计算机资讯,转载请注明出处:《Thinkphp5入门体系课程》第十三课:Migration(二)

关键词:

ThinkPHP5

本篇,我们的目标有三个: 主要: 一、TP5的目录结构和规范 project 应用部署目录 ├─application 应用目录(可设置)...

详细>>

Yolo系列其三:Yolo_v3

相信SSD的横空出世委实给了Yolo许多压力。在目标检测准确性上Yolo本来就弱于Faster-RCNN,它的提出之初就主打其能保持...

详细>>

应用预先练习互联网和脾气抽出大力进步图像识别率

大家在前几节介绍过卷积网络的运算原理,以致通过代码实施,体验到了卷积网络对图纸信息抽出的灵光。以往三个...

详细>>

CRNN论文翻译——中英文对照

CTC 的工作原理 概述 文章作者:Tyan 博客:noahsnail.com | CSDN |  简书 背景 在实际的序列学习场景中经常会遇到从没有...

详细>>