codecamp

Django Tutorial Part 3: Using models

先决条件: Django教程第2部分:创建骨架网站
目的: 为了能够设计和创建自己的模型,适当地选择字段。

概述

Django Web应用程序通过称为模型的Python对象访问和管理数据。 模型定义存储数据的结构,包括字段类型以及可能的最大大小,默认值,选择列表选项,文档帮助文本, 等等。模型的定义独立于底层数据库 - 您可以选择其中一个作为项目设置的一部分。 一旦你选择了要使用的数据库,你就不需要直接与它交谈 - 你只需要写你的模型结构和其他代码,Django处理所有与数据库沟通的肮脏的工作。

本教程介绍如何定义和访问 LocalLibrary网站的模型 例。

设计LocalLibrary模型

在你跳入并开始编码模型之前,值得花几分钟时间考虑我们需要存储什么数据以及不同对象之间的关系。

我们知道我们需要存储关于图书的信息(标题,摘要,作者,书面语言,类别,ISBN),并且我们可能有多个副本可用(具有全球唯一的ID,可用性状态等)。 我们可能需要存储有关作者的更多信息,而不仅仅是他们的姓名,并且可能有多个作者使用相同或相似的名称。 我们希望能够根据书名,作者,书面语言和类别对信息进行排序。

在设计模型时,为每个"对象"(相关信息组)分别建立模型是有意义的。 在这种情况下,明显的对象是书,书实例和作者。

您可能还想使用模型来表示选择列表选项(例如,像下拉列表中的选项),而不是将选择硬编码到网站本身 - 这是建议当所有的选项都不知道前面或可能 更改。 在这种情况下,模型的明显候选包括书种类(例如科幻小说,法国诗歌等)和语言(英语,法语,日语)。

一旦我们决定了我们的模型和领域,我们需要考虑关系。 Django允许您定义一对一( OneToOneField ),一对多( ForeignKey )和多对多( ManyToManyField )的关系。

考虑到这一点,下面的UML关联图显示了我们在这种情况下定义的模型(如框)。 如上所述,我们已经创建了书的模型(书的通用细节),书实例(系统中可用的书的特定物理副本的状态)和作者。 我们还决定了一个类型的模型,以便可以通过管理界面创建/选择值。 我们决定不要为 BookInstance:status 建立模型 - 我们已经对值进行了硬编码( LOAN_STATUS ),因为我们不希望这些值发生变化。 在每个框中,您可以看到模型名称,字段名称和类型,以及方法及其返回类型。

该图还示出了模型之间的关系,包括它们的多重性。 多重性是图上的数字,其示出了可能存在于关系中的每个模型的数量(最大和最小)。 例如,框之间的连接线表示Book和类型相关。 接近Book模型的数字表明,一本书必须有一个或多个类型(根据你的喜好),而在类型旁边的行的另一端的数字表明它可以有零个或多个相关联的书。

注意:下一节提供了一个基本的说明,说明如何定义和使用模型。 当你阅读它,考虑我们将如何构建上面的图中的每个模型。

模型引物

本节简要概述了模型的定义以及一些更重要的字段和字段参数。

模型定义

模型通常在应用程序的 models.py 文件中定义。 它们被实现为 django.db.models.Model 的基类,并且可以包括字段,方法和元数据。 下面的代码片段显示了一个"典型"模型,命名为 MyModelName :

from django.db import models

class MyModelName(models.Model):
    """
    A typical class defining a model, derived from the Model class.
    """

    # Fields
    my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")
    ...

    # Metadata
    class Meta: 
        ordering = ["-my_field_name"]

    # Methods
    def get_absolute_url(self):
         """
         Returns the url to access a particular instance of MyModelName.
         """
         return reverse('model-detail-view', args=[str(self.id)])
    
    def __str__(self):
        """
        String for representing the MyModelName object (in Admin site etc.)
        """
        return self.field_name

在下面的部分中,我们将详细探讨模型中的每个特性:

Fields

模型可以具有任意数量的任何类型的字段 - 每个字段表示我们要存储在我们的数据库表中的一列数据。 每个数据库记录(行)将由每个字段值之一组成。 让我们看看上面的例子:

my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")

我们上面的例子有一个名为my_ field_name 的字段,类型为 models.CharField - 这意味着该字段将包含字母数字字符串。 使用特定类来分配字段类型,这些类确定用于将数据存储在数据库中的记录的类型以及当从HTML表单接收到值(即,什么构成有效值)时要使用的验证标准。 字段类型还可以包含进一步指定字段如何存储或可以使用的参数。 在这种情况下,我们给我们的字段两个参数:

  • max_length=20 — States that the maximum length of a value in this field is 20 characters.
  • help_text="Enter field documentation" — provides a text label to display to help users know what value to provide when this value is to be entered by a user via an HTML form.

字段名称用于在查询和模板中引用它。 字段还有一个标签,可以指定为参数( verbose_name ),也可以通过大写字段变量名的第一个字母并用空格替换任何下划线来推断出来(例如 my_field_name / code>会有一个默认标签我的字段名称)。

如果模型以表单形式呈现(例如在管理网站中),则声明字段的顺序将影响其默认顺序,但这可能会被覆盖。

Common field arguments

在声明很多/大多数不同字段类型时,可以使用以下常见参数:

  • help_text: Provides a text label for HTML forms (e.g. in the admin site), as described above.
  • verbose_name: A human-readable name for the field used in field labels. If not specified, Django will infer the default verbose name from the field name.
  • default: The default value for the field. This can be a value or a callable object, in which case the object will be called every time a new record is created.
  • null: If True, Django will store blank values as NULL in the database for fields where this is appropriate (a CharField will instead store an empty string). The default is False.
  • blank: If True, the field is allowed to be blank in your forms. The default is False, which means that Django's form validation will force you to enter a value. This is often used with null=True , because if you're going to allow blank values, you also want the database to be able to represent them appropriately.
  • choices: A group of choices for this field. If this is provided, the default corresponding form widget will be a select box with these choices instead of the standard text field.
  • primary_key: If True, sets the current field as the primary key for the model (A primary key is a special database column designated to uniquely identify all the different table records). If no field is specified as the primary key then Django will automatically add a field for this purpose.

还有许多其他选项 - 您可以查看字段完整列表 选项。

Common field types

以下列表描述了一些更常用的字段类型。

  • CharField is used to define short-to-mid sized fixed-length strings. You must specify the max_length of the data to be stored.
  • TextField is used for large arbitrary-length strings. You may specify a max_length for the field, but this is used only when the field is displayed in forms (it is not enforced at the database level).
  • IntegerField is a field for storing integer (whole number) values, and for validating entered values as integers in forms.
  • DateField and DateTimeField are used for storing/representing dates and date/time information (as Python datetime.date in and datetime.datetime objects, respectively). These fields can additionally declare the (mutually exclusive) parameters auto_now=True (to set the field to the current date every time the model is saved), auto_now_add (to only set the date when the model is first created) , and default (to set a default date that can be overridden by the user).
  • EmailField is used to store and validate email addresses.
  • FileField and ImageField are used to upload files and images respectively (the ImageField simply adds additional validation that the uploaded file is an image). These have parameters to define how and where the uploaded files are stored.
  • AutoField is a special type of IntegerField that automatically increments. A primary key of this type is automatically added to your model if you don’t explicitly specify one.
  • ForeignKey is used to specify a one-to-many relationship to another database model (e.g. a car has one manufacturer, but a manufacturer can make many cars). The "one" side of the relationship is the model that contains the key.
  • ManyToManyField is used to specify a many-to-many relationship (e.g. a book can have several genres, and each genre can contain several books). In our library app we will use these very similarly to ForeignKeys, but they can be used in more complicated ways to describe the relationships between groups. These have the parameter on_delete to define what happens when the associated record is deleted (e.g. a value of models.SET_NULL would simply set the value to NULL).

还有许多其他类型的字段,包括用于不同类型的数字(大整数,小整数,浮点数),布尔,URL,lug,唯一ID和其他"时间相关"信息(持续时间,时间等) 。 您可以在此查看完整列表

Metadata

您可以通过声明类Meta 来为模型声明模型级元数据,如图所示。

class Meta:
    ordering = ["-my_field_name"]
    ...

此元数据的一个最有用的功能是控制在查询模型类型时返回的记录的默认排序。 您可以通过在 ordering 属性的字段名称列表中指定匹配顺序来完成此操作,如上所示。 排序将取决于字段的类型(字符字段按字母顺序排序,而日期字段按时间顺序排序)。 如上所示,您可以在字段名称前添加减号( - )以反转排序顺序。

例如,如果我们默认选择这样排序的书籍:

ordering = ["title", "-pubdate"]

书籍将按标题,从A-Z,然后在每个标题内的发布日期从字母顺序排序,从最新到最旧。

另一个常见属性是 verbose_name ,该类的详细名称为单数和复数形式:

verbose_name = "BetterName"

其他有用的属性允许您为模型创建和应用新的"访问权限"(默认权限自动应用),允许基于另一个字段排序或声明该类是"抽象"(无法创建的基类 记录,并将改为从创建其他模型中导出)。

许多其他元数据选项控制什么数据库必须用于模型以及数据如何存储(如果您需要将模型映射到现有数据库,这些仅仅有用)。

此处提供了元数据选项的完整列表:模型元数据选项( Django docs)。

Methods

模型也可以有方法。

最小的是,在每个模型中,你应该定义标准的Python类方法 __ str __()为每个对象返回一个人类可读的字符串。 此字符串用于表示管理站点(以及需要引用模型实例的任何其他位置)中的各个记录。 通常,这将从模型返回标题或名称字段。

def __str__(self):
    return self.field_name

在Django模型中包含的另一种常见方法是 get_absolute_url(),它返回一个用于在网站上显示各个模型记录的URL(如果你定义了这个方法,Django会自动添加一个"View on Site"按钮 到管理站点中模型的记录编辑屏幕)。 get_absolute_url()的典型模式如下所示。

def get_absolute_url(self):
    """
    Returns the url to access a particular instance of the model.
    """
    return reverse('model-detail-view', args=[str(self.id)])

注意:假设您将使用像 / myapplication / mymodelname / 2 这样的网址来显示模型的各个记录(其中"2"是 id 对于特定的记录),您将需要创建一个URL映射器将响应和id传递到"模型详细视图"(它将完成显示记录所需的工作)。 上面的 reverse()函数能够"反转"你的url映射器(在上面的例子中名为\'model-detail-view\'), 正确的格式

当然要做这个工作,你还要写URL映射,视图和模板!

您还可以定义任何其他方法,并从代码或模板中调用它们(前提是它们不带任何参数)。

模型管理

定义模型类后,可以使用它们创建,更新或删除记录,并运行查询以获取所有记录或记录的特定子集。 我们将在教程中定义视图时告诉您如何做,但这里是一个简要的摘要。

Creating and modifying records

要创建记录,您可以定义模型的实例,然后调用 save()

# Create a new record using the model's constructor.
a_record = MyModelName(my_field_name="Instance #1")

# Save the object into the database.
a_record.save()

注意:如果您尚未将任何字段声明为 primary_key ,则会自动给出新记录,字段名称为 id 。 您可以在保存以上记录后查询此字段,它的值为1。

您可以使用点语法访问此新记录中的字段,并更改值。 您必须调用 save()才能将修改的值存储到数据库。

# Access model field values using Python attributes.
print(a_record.id) #should return 1 for the first record. 
print(a_record.my_field_name) # should print 'Instance #1'

# Change record by modifying the fields, then calling save().
a_record.my_field_name="New Instance Name"
a_record.save()

Searching for records

您可以使用模型的 objects 属性(由基类提供)搜索符合特定条件的记录。

注意:解释如何使用"抽象"模型和字段名称搜索记录可能会有点混乱。 在下面的讨论中,我们将使用 title genre 字段引用 Book 模型,其中genre也是一个模型, code> name 。

我们可以使用 objects.all()获得模型的所有记录作为 QuerySet QuerySet 是一个可迭代的对象,意味着它包含一些对象,我们可以迭代/循环。

all_books = Book.objects.all()

Django的 filter()方法允许我们过滤返回的 QuerySet 以匹配指定的文本数字 特定标准。 例如,要筛选标题中包含"wild"的书籍,然后对其计数,我们可以执行以下操作。

wild_books = Book.objects.filter(title__contains='wild')
number_wild_books = Book.objects.filter(title__contains='wild').count()

匹配的字段和匹配类型在过滤器参数名称中使用以下格式定义: field_name__match_type (注意 title 之间的 和包含上面的)。 上面我们使用区分大小写的匹配过滤 title 。 还有许多其他类型的匹配可以做: icontains (不区分大小写), iexact (case-insenstive完全匹配), exact 敏感的完全匹配),, gt (大于), startswith 等。 .djangoproject.com / en / 1.10 / ref / models / querysets /#field-lookups"class ="external">完整列表在这里

在某些情况下,您需要对定义与另一个模型的一对多关系(例如 ForeignKey )的字段进行过滤。 在这种情况下,您可以使用附加的双下划线"索引"到相关模型中的字段。 因此,例如要筛选具有特定类型模式的书籍,您必须通过 genre 字段将 name 编入索引,如下所示:

books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')  # Will match on: Fiction, Science fiction, non-fiction etc.

注意:您可以根据需要使用下划线(__)导航多级关系( ForeignKey / ManyToManyField )。 例如,具有不同类型的Book,使用另一个"cover"关系定义可能有一个参数名: type__cover__name__exact =\'hard\'。

还有很多你可以做的查询,包括从相关模型向后搜索,链接过滤器,返回一个较小的值集等。更多信息,请参阅 1.10 / topics / db / queries /"class ="external">进行查询(Django Docs)。

定义LocalLibrary模型

在本节中,我们将开始定义库的模型。 打开 models.py(在/ locallibrary / catalog /)。 页面顶部的样板导入了模型模块,其中包含模型将继承的模型基类 models.Model

from django.db import models

# Create your models here.

类型模型

复制下面显示的类型模型代码,并将其粘贴到 models.py 文件的底部。 该模型用于存储关于书类别的信息 - 例如它是小说还是非小说,浪漫或军事历史等。如上所述,我们创建了类型作为模型而不是自由文本或 选择列表,使得可以通过数据库管理可能的值,而不是硬编码。

class Genre(models.Model):
    """
    Model representing a book genre (e.g. Science Fiction, Non Fiction).
    """
    name = models.CharField(max_length=200, help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)")
    
    def __str__(self):
        """
        String for representing the Model object (in Admin site etc.)
        """
        return self.name

模型有一个 CharField 字段( name ),用于描述类型(这限制为200个字符,并且有一些 help_text 。在模型的末尾我们声明一个 __ str __()方法,它只返回一个特定记录定义的类型的名字,没有定义详细的名字, 代码>名称。

书模型

复制下面的Book模型,然后将其粘贴到文件的底部。 书模型表示关于一般意义上的可用书的所有信息,而不是可用于贷款的特定实体"实例"或"拷贝"。 该模型使用 CharField 代表书的 title isbn (注意 isbn ISBN"使用第一未命名参数,因为默认标签否则将是"Isbn")。 该模型对 summary 使用 TextField ,因为此文本可能需要很长时间。

from django.urls import reverse #Used to generate URLs by reversing the URL patterns

class Book(models.Model):
    """
    Model representing a book (but not a specific copy of a book).
    """
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
    # Foreign Key used because book can only have one author, but authors can have multiple books
    # Author as a string rather than object because it hasn't been declared yet in the file.
    summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
    isbn = models.CharField('ISBN',max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
    genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
    # ManyToManyField used because genre can contain many books. Books can cover many genres.
    # Genre class has already been defined so we can specify the object above.
    
    def __str__(self):
        """
        String for representing the Model object.
        """
        return self.title
    
    
    def get_absolute_url(self):
        """
        Returns the url to access a particular book instance.
        """
        return reverse('book-detail', args=[str(self.id)])

类型是一个 ManyToManyField ,这样一本书可以有多种类型,一种类型可以有很多书。 作者被声明为 ForeignKey ,所以每本书只有一个作者,但作者可能有很多书(实际上一本书可能有多个作者,但不是在这个实现!

在两种字段类型中,相关模型类都使用模型类或包含相关模型名称的字符串声明为第一个未命名参数。 如果在引用之前尚未在此文件中定义关联类,则必须将模型的名称用作字符串! author 字段中感兴趣的其他参数是 null = True ,如果未选择作者,则允许数据库存储 Null on_delete = models.SET_NULL ,如果关联的作者记录被删除,它将设置作者的值为 Null

该模型还定义 __ str __(),使用书的 title 字段表示 Book 记录。 最后一个方法, get_absolute_url()返回一个URL,可以用来访问这个模型的详细记录(为了这个工作,我们需要定义一个URL映射,名字为 book -detail ,并定义关联的视图和模板)。

BookInstance模型

接下来,复制其他模型下的 BookInstance 模型(如下所示)。 BookInstance 表示某人可能借用的书的特定副本,并且包括关于副本是否可用的信息,或者预期返回什么日期,"印记"或版本详细信息,以及唯一ID 为图书馆的书。

现在一些领域和方法将是熟悉的。 模型使用

  • ForeignKey to identify the associated Book (each book can have many copies, but a copy can only have one Book).
  • CharField to represent the imprint (specific release) of the book.
import uuid # Required for unique book instances

class BookInstance(models.Model):
    """
    Model representing a specific copy of a book (i.e. that can be borrowed from the library).
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Unique ID for this particular book across whole library")
    book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True) 
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('d', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='d', help_text='Book availability')

    class Meta:
        ordering = ["due_back"]
        

    def __str__(self):
        """
        String for representing the Model object
        """
        return '%s (%s)' % (self.id,self.book.title)

我们另外声明几种新类型的字段:

  • UUIDField is used for the id field to set it as the primary_key for this model. This type of field allocates a globally unique value for each instance (one for every book you can find in the library).
  • DateField is used for the due_back date (at which the book is expected to come available after being borrowed or in maintenance). This value can be blank or null (needed for when the book is available). The model metadata (Class Meta) uses this field to order records when they are returned in a query.
  • status is a CharField that defines a choice/selection list. As you can see, we define a tuple containing tuples of key-value pairs and pass it to the choices argument. The value in a key/value pair is a display value that a user can select, while the keys are the values that are actually saved if the option is selected. We've also set a default value of 'd' (maintenance) as books will initially be created unavailable before they are stocked on the shelves.

模型 __ str __()表示使用其唯一ID和相关联的 Book 标题的组合的 BookInstance 对象。

注意:一点Python:

  • The value returned by __str__() is a formatted string. Within the string we use %s to declare "placeholders'. After the string we specify % and then a tuple containing the values to be inserted in the placeholders. If you just have one placeholder then you can omit the tuple - e.g. 'My value: %s' % variable

作者模型

复制 models.py 中现有代码下的作者模型(如下所示)。

所有的字段/方法现在应该是熟悉的。 该模型定义作者具有名字,姓氏,出生日期和(可选)死亡日期。 它指定默认情况下 __ str __()返回姓氏 firstname 顺序中的名称。 get_absolute_url()方法反转 author-detail URL映射,以获取显示单个作者的URL。

class Author(models.Model):
    """
    Model representing an author.
    """
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)
    
    def get_absolute_url(self):
        """
        Returns the url to access a particular author instance.
        """
        return reverse('author-detail', args=[str(self.id)])
    

    def __str__(self):
        """
        String for representing the Model object.
        """
        return '%s, %s' % (self.last_name, self.first_name)

重新运行数据库迁移

所有的模型现在已经创建。 现在重新运行数据库迁移,将它们添加到数据库中。

python3 manage.py makemigrations
python3 manage.py migrate

语言模型 - 挑战

想象一下,一个地方捐赠者捐赠了一些用另一种语言写的新书(比如说,波斯语)。 挑战是如何在我们的图书馆网站中最好地展示这些,然后将它们添加到模型中。

有些事情要考虑:

  • Should "language" be associated with a Book, BookInstance, or some other object?
  • Should the different languages be represented using model, a free text field, or a hard-coded selection list?

确定后,添加字段。 您可以查看我们在Github上决定的此处

      概要

      在本文中,我们了解了如何定义模型,然后使用此信息为 LocalLibrary 网站设计和实施适当的模型。

      此时,我们将暂时从创建网站中转移,并查看 Django管理网站。 这个网站将允许我们向库中添加一些数据,然后我们可以使用我们的(尚未创建的)视图和模板来显示。

      也可以看看

      Django web application security
      Assessment: DIY Django mini blog
      温馨提示
      下载编程狮App,免费阅读超1000+编程语言教程
      取消
      确定
      目录
      CSS

      关闭

      MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }