# 模型（一）

困惑：

在模型这一部分，根据具体的要求建立起数据模型，对应数据库中的结构，其实并不难，当然他涉及到一些数据库的问题，如果我们后面对数据库的了解更加深入一些，基本上是不会有什么问题的。但是让我觉得很奇怪的，也是不断让我让我头晕的是这些模型对象的方法，感觉完全没有规律，可以查询，可以添加，可以删除等等，而且所使用的方法的名称也不是固定的，而是会更根据字段的名称变化，这是我见过最复杂的类方法。

在另一方面，我们会在哪里使用到这些模型方法呢？几乎所有涉及到数据的地方，因此能否掌握这一部分关系到对数据的灵活运用。

## 一、基础

1、每个模型都是一个python的类，继承于django.db.models.Model

2、模型类中的每一个属性都相当于数据库中的一个字段

示例

```python
from django.db import models

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

## 二、使用模型

建立模型之后，要在settings文件中的INSTALLED\_APPS中添加应用的名字。

## 三、字段

1、注意定义字段名时不要与models API冲突

2、字段类型

* 字段类型用于指定数据库数据类型（如INTEGER、VARCHAR、TEXT等）
* 默认的HTML表单输入框（如：\<input type="text">\<select>）
* 用于Django admin和自动生成表单的基本验证

> 确实有些不懂，但最重要还是第一条

3、字段选项

在设置某一个字段的时候，可能它对应的类型需要一些参数，比如varchar会需要传入一个max\_length参数来指定字符串长度，其中有些通用的参数如下。

* null

如果设置为True，当字段为空的时候，django会将其默认为NULL，默认情况为False

* blank

若为True，则允许字段为空，默认为False

null选项是数据库层main的设置，而blank是涉及到**表单验证**方面的，如果一个字段设置blank=True，**在表单验证时，接受的数据该字段值允许为空，**

> ok，我的理解是这样的。在一些网站上，我们经常看到一些选项框，你可以往里面输入一些内容，然后提交，有时候，其中一些项是可以为空的，而这些为空的数据项，就应该是上面提到的。可是，我不太理解，模型字段的数据类型与数据库数据类型有关是理所应当的，但是为什么会与表单提交的数据有关，除非是将提交的表单数据，接受之后存储在模型定义的字段中，但即便这样也感觉很奇怪。

* choices

该参数接受一个列表或元组（基本单位为二元组），如果指定了该参数，在实例化该模型时，该字段只能选取列表中的值。

```python
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, chocies=SHIRT_SIZES)
```

```
>>> p = Person(name = "Fred Flinstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
```

二元组的第一个值存储在数据库中，第二个值用于显示

> 等等，这个好神奇啊，p.get\_*xxxx*\_display()，这本来应该是一个方法，合理一点的方式是，（中间的下划线不能打出）p.getdisplay(xxxx)。但是它现在却把它放在了前面，这是怎么做到的！！？？

* default

默认值或者可调用的对象

* help\_text

帮助说明，等等，为什么这也与表单有关？

* primary\_key

如果为True， 该字段设置为模型的主键。如果没有设置主键，django会自动创建一个INTERGER的主键字段。**主键字段是只可读的，如果修改一个主键字段的值并保存，其实是创建了一个新的模型实例。**

* unique

如果为True，字段中的值不能重复，每个值都是唯一的。

## 4、自动设置主键

默认情况下，django会给每一个模型添加一个字段

```
id = models.AutoField(primary_key=True)
```

## 5、备注名

除了ForeignKey，ManyToField和OneToOneField，其他字段类型都接受一个可选的参数verbose\_name，默认为字段的属性名

```
first_name = models.CharField("person's first name", max_length=30)
# 备注名为person's first name
first_name = models.CharField(max_length=30)
# 备注名为first_name
```

上面提到的三个字段类型接收的一个参数为模型的类名，比如外键的那个，第一个参数就是外键连接的模型的类，后面依旧可以添加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')
```

## 6、关联关系

django提供了定义最常见的数据库关联关系的方法：多对一，多对多，一对一

1、多对一

简单地使用django.db.models.ForeignKey类，也就是外键

```python
from django.db import models

class Manufacturer(models.Model):
    pass

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

 # on_delete=models.CASCADE   删除关联数据,与之关联也删除
```

2、多对多

需要使用django.db.models.ManyToManyField类

```python
from django.db import models

class Topping(models.Model):
    pass

class Pizza(models.Model):
    topping = models.ManyToManyField(Topping)
```

> 这里不太懂，到底哪里体现出多对多的关系的

对于多对多关联关系的两个模型，可以在其中任意一个添加ManyToMany字段，但只能选择一个模型设置该字段。

一个简单的多对多模型只要上面那样就可以了，但是有时候我们可能会需要一个额外的关系表来记录两个模型之间的关系。

```python
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)
```

> 看起来多对多的时候，一个ManyToMany字段中应该会有多个值，那输入的时候会输入一个数组吗？加上through之后，又有什么变化吗？

原文中提到，Membership这个表中如果有且只有一个外键指向原表（在这里是指Group）的话，Group的members是可以不加through的，这很神奇啊！如果没有through，从外面看起来，Person和Group这两个表是再正常不过的表，他们是如何和Membership联系起来的呢？

> 这里外键原文中也很模糊，前面说是指向原表的外键，可是为什么一个关系表中会有两个外键指向原表？这很奇怪啊？如果原文的意思是说多个外键的话，更加奇怪了，关系表本是就是描述两个表的关系，肯定会有两个外键分表指向两个表，怎么可能只有一个外键？所以看来还是有多个指向原表的外键更加正确，假设有group1和group2两个指向原表的外键，这时需要models.ManyToManyField.through\_fields来指定使用哪个字段，但问题还是，这两个外键怎么会有差别呢？除了字段名称？

如果使用了关系表后，就不能使用add等方法来操作person和group，因为他们的关系是通过一个中间的关系表确定，必须在关系表中操作才可以。

此处涉及到很多关系表的方法，但是太杂乱。

3、一对一

通过OneToOneField定义字段，就可以创建一对一模型。使用的情况为：假设你创建了一个‘place’的数据库，每一行记录包括地址、电话号码、邮政编码等等信息，然后有一个餐馆刚好就在这个地址上面，这时就需要一对一模型了。

## 7、跨应用导入模型

```python
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)
```

感觉这个很实用，但是明显感觉只能在一个服务器上这样使用

## 8、字段名限制

1、不能使用关键字（保留字）

2、不能包括两个下划线

## 9、自定义字段

django允许自定义字段类型

## 10、meta选项

```python
from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ['horn_length']
        verbose_name_plural = 'oxen'
```

meta选项好像是一些基础设置，比如数据库名称，阅读性等方面的设置。

## 11、模型方法

简单说就是类方法，只不过这个类是数据库模型

```python
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharFiedl(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"
```

两个很重要的方法

1、魔法方法str

返回字符串形式的表达

2、get(下划线)absolute\_url()

任何拥有唯一被定义的url的对象都应该定义这个方法

## 12、覆盖预定义的模型方法

我之前提到比较复杂的就是这一类

有一些模型内置的预定义的方法，但某些情况下可能需要自定义，比如save()或者delete()

```python
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tageline = models.TextField()

    def save(self, *args. **kargs):
        do_something()
        super().save(*args, **kwargs)
        do_something_else()
```

do\_something，可以的 ^-^

重点在于调用super函数来完成保存的操作，还有修改预定义的方法一定要注意测试，因为django很复杂，有可能涉及到其他的方法的调用，比如说调用到某个方式时，它可能内部用到了save方法，这种情况要考虑在内。

## 13、执行自定义的sql语句

## 14、模型继承

因为模型与数据库有关，所以当继承一个父类的时候，根据不同的要求，希望对数据库产生的影响也是不同的。

情况一：继承父类是为了继承某个所有子类都会有的特性，父类仅仅是存储信息，而这个类不会单独使用，这时候可以使用abstruct base classes

情况二：如果想继承一个已存在的类，这个类往往已经在其他应用中被使用了，然而你希望新的类和父类存在各自的数据库中，这时候可以选择multi-table inheritance

情况三：如果你只是想在python编程上修改一个模型，不涉及到以任何方式改变任何字段，可以使用prox models

第三种情况不太懂，那这个类到底是用来干嘛的，玩吗？

> abstruct base：抽象类

在普通的python类中，子类可以重写父类的属性，但是在django中，这个通常是不被允许的。如果父类是一个非抽象类，其中有一个字段为author，那么子类中不可以重新定义一个author字段。如果是抽象类，则可以。

## 15、将模型打包

如果你对应用中有很多个models.py，可以创建一个models文件夹，然后创建一个初始化的init文件

```python
# __init__.py
from .organic import Person
from .synthetic import Robot
```
