Py西游攻關之Django(一)


     課程簡介:

  • Django流程介紹
  • Django url
  • Django view
  • Django models
  • Django template
  • Django form
  • Django admin (后台數據庫管理工具)

 

1 Django流程介紹

MTV模式      

        著名的MVC模式:所謂MVC就是把web應用分為模型(M),控制器(C),視圖(V)三層;他們之間以一種插件似的,松耦合的方式連接在一起。

        模型負責業務對象與數據庫的對象(ORM),視圖負責與用戶的交互(頁面),控制器(C)接受用戶的輸入調用模型和視圖完成用戶的請求。

        

       Django的MTV模式本質上與MVC模式沒有什么差別,也是各組件之間為了保持松耦合關系,只是定義上有些許不同,Django的MTV分別代表:

       Model(模型):負責業務對象與數據庫的對象(ORM)

       Template(模版):負責如何把頁面展示給用戶

       View(視圖):負責業務邏輯,並在適當的時候調用Model和Template

       此外,Django還有一個url分發器,它的作用是將一個個URL的頁面請求分發給不同的view處理,view再調用相應的Model和Template

 

2 Django URL

URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL模式以及要為該URL模式調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。URL的家在是從配置文件中開始。

參數說明:

  • 一個正則表達式字符串
  • 一個可調用對象,通常為一個視圖函數或一個指定視圖函數路徑的字符串
  • 可選的要傳遞給視圖函數的默認參數(字典形式)
  • 一個可選的name參數

2.1  Here’s a sample URLconf:

from django.conf.urls import url
 
from . import views
 
urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

Notes:

  • To capture a value from the URL, just put parenthesis around it.
  • There’s no need to add a leading slash, because every URL has that. For example, it’s ^articles, not ^/articles.
  • The 'r' in front of each regular expression string is optional but recommended. It tells Python that a string is “raw” – that nothing in the string should be escaped. See Dive Into Python’s explanation.

Example requests:

  • A request to /articles/2005/03/ would match the third entry in the list. Django would call the functionviews.month_archive(request, '2005', '03').
  • /articles/2005/3/ would not match any URL patterns, because the third entry in the list requires two digits for the month.
  • /articles/2003/ would match the first pattern in the list, not the second one, because the patterns are tested in order, and the first one is the first test to pass. Feel free to exploit the ordering to insert special cases like this. Here, Django would call the function views.special_case_2003(request)
  • /articles/2003 would not match any of these patterns, because each pattern requires that the URL end with a slash.
  • /articles/2003/03/03/ would match the final pattern. Django would call the functionviews.article_detail(request, '2003', '03', '03').

2.2 Named groups

The above example used simple, non-named regular-expression groups (via parenthesis) to capture bits of the URL and pass them as positional arguments to a view. In more advanced usage, it’s possible to use named regular-expression groups to capture URL bits and pass them as keyword arguments to a view.

In Python regular expressions, the syntax for named regular-expression groups is (?P<name>pattern), where name is the name of the group and pattern is some pattern to match.

Here’s the above example URLconf, rewritten to use named groups:

import re

ret=re.search('(?P<id>\d{3})/(?P<name>\w{3})','weeew34ttt123/ooo')

print(ret.group())
print(ret.group('id'))
print(ret.group('name'))
正則知識
from django.conf.urls import url
 
from . import views
 
urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

This accomplishes exactly the same thing as the previous example, with one subtle difference: The captured values are passed to view functions as keyword arguments rather than positional arguments. For example:

  • A request to /articles/2005/03/ would call the function views.month_archive(request, year='2005',month='03'), instead of views.month_archive(request, '2005', '03').
  • A request to /articles/2003/03/03/ would call the function views.article_detail(request, year='2003',month='03', day='03').

In practice, this means your URLconfs are slightly more explicit and less prone to argument-order bugs – and you can reorder the arguments in your views’ function definitions. Of course, these benefits come at the cost of brevity; some developers find the named-group syntax ugly and too verbose.

常見寫法實例:

2.3  Captured arguments are always strings

Each captured argument is sent to the view as a plain Python string, regardless of what sort of match the regular expression makes. For example, in this URLconf line:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
...the  year argument passed to  views.year_archive() will be a string,
not an integer, even though the  [0-9]{4} will only match integer strings.

2.4  Including other URLconfs 

At any point, your urlpatterns can “include” other URLconf modules. This essentially “roots” a set of URLs below other ones.

For example, here’s an excerpt of the URLconf for the Django website itself. It includes a number of other URLconfs:

from django.conf.urls import include, url
 
urlpatterns = [
    # ... snip ...
    url(r'^community/', include('django_website.aggregator.urls')),
    url(r'^contact/', include('django_website.contact.urls')),
    # ... snip ...
]

2.5 Passing extra options to view functions

URLconfs have a hook that lets you pass extra arguments to your view functions, as a Python dictionary.

The django.conf.urls.url() function can take an optional third argument which should be a dictionary of extra keyword arguments to pass to the view function.

For example:

from django.conf.urls import url
from . import views
 
urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

In this example, for a request to /blog/2005/, Django will call views.year_archive(request, year='2005',foo='bar').

This technique is used in the syndication framework to pass metadata and options to views.

Dealing with conflicts

It’s possible to have a URL pattern which captures named keyword arguments, and also passes arguments with the same names in its dictionary of extra arguments. When this happens, the arguments in the dictionary will be used instead of the arguments captured in the URL.

需要注意的是,當你加上參數時,對應函數views.index必須加上一個參數,參數名也必須命名為a,如下:

 

#應用:

if  auth():

    obj=model.user.filter()

{'obj':obj}

2.6 name param

urlpatterns = [
    url(r'^index',views.index,name='bieming'),
    url(r'^admin/', admin.site.urls),
    # url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    # url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    # url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),

]
###################

def index(req):
    if req.method=='POST':
        username=req.POST.get('username')
        password=req.POST.get('password')
        if username=='alex' and password=='123':
            return HttpResponse("登陸成功")



    return render(req,'index.html')

#####################

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#     <form action="/index/" method="post">#}
     <form action="{% url 'bieming' %}" method="post">
         用戶名:<input type="text" name="username">
         密碼:<input type="password" name="password">
         <input type="submit" value="submit">
     </form>
</body>
</html>


#######################
name的應用

 

3 Django Views(視圖函數)

七  視圖函數

http請求中產生兩個核心對象:

http請求:HttpRequest對象

http響應:HttpResponse對象

所在位置:django.http

之前我們用到的參數request就是HttpRequest    檢測方法:isinstance(request,HttpRequest)

 

1 HttpRequest對象的屬性:

 

# path:       請求頁面的全路徑,不包括域名
#
# method:     請求中使用的HTTP方法的字符串表示。全大寫表示。例如
#
#                    if  req.method=="GET":
#
#                              do_something()
#
#                    elseif req.method=="POST":
#
#                              do_something_else()
#
# GET:         包含所有HTTP GET參數的類字典對象
#
# POST:       包含所有HTTP POST參數的類字典對象
#
#              服務器收到空的POST請求的情況也是可能發生的,也就是說,表單form通過
#              HTTP POST方法提交請求,但是表單中可能沒有數據,因此不能使用
#              if req.POST來判斷是否使用了HTTP POST 方法;應該使用  if req.method=="POST"
#
#
#
# COOKIES:     包含所有cookies的標准Python字典對象;keys和values都是字符串。
#
# FILES:      包含所有上傳文件的類字典對象;FILES中的每一個Key都是<input type="file" name="" />標簽中                     name屬性的值,FILES中的每一個value同時也是一個標准的python字典對象,包含下面三個Keys:
#
#             filename:      上傳文件名,用字符串表示
#             content_type:   上傳文件的Content Type
#             content:       上傳文件的原始內容
#
#
# user:       是一個django.contrib.auth.models.User對象,代表當前登陸的用戶。如果訪問用戶當前
#              沒有登陸,user將被初始化為django.contrib.auth.models.AnonymousUser的實例。你
#              可以通過user的is_authenticated()方法來辨別用戶是否登陸:
#              if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
#              時該屬性才可用
#
# session:    唯一可讀寫的屬性,代表當前會話的字典對象;自己有激活Django中的session支持時該屬性才可用。
View Code

HttpRequest對象的方法:get_full_path(),   比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的結果就是/index33/?name=123

 

 2 HttpResponse對象:

   對於HttpRequest對象來說,是由django自動創建的,但是,HttpResponse對象就必須我們自己創建。每個view請求處理方法必須返回一個HttpResponse對象。

  HttpResponse類在django.http.HttpResponse

  在HttpResponse對象上擴展的常用方法:頁面渲染:render,render_to_response,

                                                        頁面跳轉:redirect

                                                        locals:   可以直接將函數中所有的變量傳給模板    

   

 

4 Django Models

4.1 數據庫配置  

1      django默認支持sqlite,mysql, oracle,postgresql數據庫。

    <1> sqlite

            django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動

            引擎名稱:django.db.backends.sqlite3

     <2>mysql

            引擎名稱:django.db.backends.mysql

2    mysql驅動程序

          MySQLdb(mysql python)

          mysqlclient

          MySQL

          PyMySQL(純python的mysql驅動程序)

3     在django的項目中會默認使用sqlite數據庫,在settings里有如下設置:

           

              如果我們想要更改數據庫,需要修改如下:

             

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'django_com',
        'USER':'root',
        'PASSWORD':'',

    }
}
對應代碼

 

 注意:NAME即數據庫的名字,在mysql連接前該數據庫必須已經創建,而上面的sqlite數據庫下的db.sqlite3則是項目自動創建

         USER和PASSWORD分別是數據庫的用戶名和密碼。

         設置完后,再啟動我們的Django項目前,我們需要激活我們的mysql。

         然后,啟動項目,會報錯:no module named MySQLdb

         這是因為django默認你導入的驅動是MySQLdb,可是MySQLdb對於py3有很大問題,所以我們需要的驅動是PyMySQL

         所以,我們只需要找到項目名文件下的__init__,在里面寫入:

                 import pymysql

                 pymysql.install_as_MySQLdb()

         問題就解決了!

         這時就可以正常啟動了。

        但此時數據庫內並沒有內容,我們需要做數據庫的同步:

        


       為了更好的查詢修改數據庫,我們可以不使用Navicate,而是利用pycharm的Database(愛死pycharm啦)

       

      然后 ,安裝MySQL的驅動(driver),這里需要創建一個密碼(我的是123)安裝成功后,

       

      填入數據庫的名字,mysql的用戶名和密碼,然后就可以進行連接了。

       

      成功后點擊右下角的apply和OK。

      這是你就可以看到數據庫里的表和內容了:

       

      是不是很方便呢?

      如果你用的是sqlite數據庫就更簡單了,安裝完驅動后,直接將sqlite拖動到Database就可以了:

      

 


 

4.2  Django的ORM(關系對象映射)

4.2.1  模型類的定義(一)

 

用於實現面向對象編程語言里不同類型系統的數據之間的轉換,換言之,就是用面向對象的方式去操作數據庫的創建表以及增刪改查等操作。

 優點:1 ORM使得我們的通用數據庫交互變得簡單易行,而且完全不用考慮該死的SQL語句。快速開發,由此而來。

          2 可以避免一些新手程序猿寫sql語句帶來的性能問題。

            比如 我們查詢User表中的所有字段:

            

            新手可能會用select * from  auth_user,這樣會因為多了一個匹配動作而影響效率的。

 缺點:1 性能有所犧牲,不過現在的各種ORM框架都在嘗試各種方法,比如緩存,延遲加載登來減輕這個問題。效果                 很顯著。

           2 對於個別復雜查詢,ORM仍然力不從心,為了解決這個問題,ORM一般也支持寫raw sql。

 

 

下面要開始學習Django ORM語法了,為了更好的理解,我們來做一個基本的 書籍/作者/出版商 數據庫結構。 我們這樣做是因為 這是一個眾所周知的例子,很多SQL有關的書籍也常用這個舉例。

實例:我們來假定下面這些概念,字段和關系

作者模型:一個作者有姓名。

作者詳細模型:把作者的詳情放到詳情表,包含性別,email地址和出生日期,作者詳情模型和作者模型之間是一對一的關系(one-to-one)(類似於每個人和他的身份證之間的關系),在大多數情況下我們沒有必要將他們拆分成兩張表,這里只是引出一對一的概念。

出版商模型:出版商有名稱,地址,所在城市,省,國家和網站。

書籍模型:書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關系就是多對多的關聯關系(many-to-many),一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關系(one-to-many),也被稱作外鍵。

from __future__ import unicode_literals

from django.db import models

# Create your models here.

from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名稱")
    address = models.CharField("地址", max_length=50)
    city = models.CharField('城市',max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()

    class Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=30)
    def __str__(self):
        return self.name

class AuthorDetail(models.Model):
    sex = models.BooleanField(max_length=1, choices=((0, ''),(1, ''),))
    email = models.EmailField()
    address = models.CharField(max_length=50)
    birthday = models.DateField()
    author = models.OneToOneField(Author)

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,default=10)
    def __str__(self):
        return self.title
對應代碼

記得在settings里的INSTALLED_APPS中加入'app01',然后同步數據庫:

 

分析代碼:

        1  每個數據模型都是django.db.models.Model的子類,它的父類Model包含了所有必要的和數據庫交互的方法。並提供了一個簡介漂亮的定義數據庫字段的語法。

        2  每個模型相當於單個數據庫表(多對多關系例外,會多生成一張關系表),每個屬性也是這個表中的字段。屬性名就是字段名,它的類型(例如CharField)相當於數據庫的字段類型(例如varchar)。大家可以留意下其它的類型

都和數據庫里的什么字段對應。

         3  模型之間的三種關系:一對一,一對多,多對多。

             一對一:實質就是在主外鍵(author_id就是foreign key)的關系基礎上,給外鍵加了一個UNIQUE的屬性;

           

            一對多:就是主外鍵關系;

                               

           

             多對多:

                     book類里定義了一個多對多的字段authors,並沒在book表中,這是因為創建了一張新的表:

                                              

         4  模型的常用字段類型以及參數:

# AutoField
# 一個 IntegerField, 添加記錄時它會自動增長. 你通常不需要直接使用這個字段; 如果你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.(參閱 _自動主鍵字段)
# BooleanField
# A true/false field. admin 用 checkbox 來表示此類字段.
# CharField
# 字符串字段, 用於較短的字符串.
# 
# 如果要保存大量文本, 使用 TextField.
# 
# admin 用一個 <input type="text"> 來表示此類字段 (單行輸入).
# 
# CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所允許的最大字符數.
# 
# CommaSeparatedIntegerField
# 用於存放逗號分隔的整數值. 類似 CharField, 必須要有 maxlength 參數.
# DateField
# 一個日期字段. 共有下列額外的可選參數:
# 
# Argument    描述
# auto_now    當對象被保存時,自動將該字段的值設置為當前時間.通常用於表示 "last-modified" 時間戳.
# auto_now_add    當對象首次被創建時,自動將該字段的值設置為當前時間.通常用於表示對象創建時間.
# admin 用一個文本框 <input type="text"> 來表示該字段數據(附帶一個 JavaScript 日歷和一個"Today"快鍵.
# 
# DateTimeField
#  一個日期時間字段. 類似 DateField 支持同樣的附加選項.
# admin 用兩上文本框 <input type="text"> 表示該字段順序(附帶JavaScript shortcuts). 
# 
# EmailField
# 一個帶有檢查 Email 合法性的 CharField,不接受 maxlength 參數.
# FileField
# 一個文件上傳字段.
# 
# 要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime formatting, 該格式將被上載文件的 date/time 替換(so that uploaded files don't fill up the given directory).
# 
# admin 用一個``<input type="file">``部件表示該字段保存的數據(一個文件上傳部件) .
# 
# 在一個 model 中使用 FileField 或 ImageField 需要以下步驟:
# 
# 在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件. (出於性能考慮,這些文件並不保存到數據庫.) 定義 MEDIA_URL 作為該目錄的公共 URL. 要確保該目錄對 WEB 服務器用戶帳號是可寫的.
# 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django 使用 MEDIA_ROOT 的哪個子目錄保存上傳文件.
# 你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT). 出於習慣你一定很想使用 Django 提供的 get_<fieldname>_url 函數.舉例來說,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object.get_mug_shot_url }} 這樣的方式得到圖像的絕對路徑.
# FilePathField
# 可選項目為某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的.
# 
# 參數    描述
# path    必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此得到可選項目. Example: "/home/images".
# match    可選參數. 一個正則表達式, 作為一個字符串, FilePathField 將使用它過濾文件名. 注意這個正則表達式只會應用到 base filename 而不是路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif.
# recursive    可選參數.要么 True 要么 False. 默認值是 False. 是否包括 path 下面的全部子目錄.
# 這三個參數可以同時使用.
# 
# 我已經告訴過你 match 僅應用於 base filename, 而不是路徑全名. 那么,這個例子:
# 
# FilePathField(path="/home/images", match="foo.*", recursive=True)
# ...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
# 
# FloatField
# 一個浮點數. 必須 提供兩個 參數:
# 
# 參數    描述
# max_digits    總位數(不包括小數點和符號)
# decimal_places    小數位數
# 舉例來說, 要保存最大值為 999 (小數點后保存2位),你要這樣定義字段:
# 
# models.FloatField(..., max_digits=5, decimal_places=2)
# 要保存最大值一百萬(小數點后保存10位)的話,你要這樣定義:
# 
# models.FloatField(..., max_digits=19, decimal_places=10)
# admin 用一個文本框(<input type="text">)表示該字段保存的數據.
# 
# ImageField
# 類似 FileField, 不過要校驗上傳對象是否是一個合法圖片.它有兩個可選參數:height_field 和 width_field,如果提供這兩個參數,則圖片將按提供的高度和寬度規格保存.
# 
# 該字段要求 Python Imaging Library.
# 
# IntegerField
# 用於保存一個整數.
# 
# admin 用一個``<input type="text">``表示該字段保存的數據(一個單行編輯框)
# 
# IPAddressField
# 一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
# 
# admin 用一個``<input type="text">``表示該字段保存的數據(一個單行編輯框)
# 
# NullBooleanField
# 類似 BooleanField, 不過允許 NULL 作為其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項.
# 
# admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據.
# 
# PhoneNumberField
# 一個帶有合法美國風格電話號碼校驗的 CharField``(格式: ``XXX-XXX-XXXX).
# PositiveIntegerField
# 類似 IntegerField, 但取值范圍為非負整數(這個字段應該是允許0值的....所以字段名字取得不太好,無符號整數就對了嘛).
# PositiveSmallIntegerField
# 類似 PositiveIntegerField, 取值范圍較小(數據庫相關)
# SlugField
# "Slug" 是一個報紙術語. slug 是某個東西的小小標記(短簽), 只包含字母,數字,下划線和連字符.它們通常用於URLs.
# 
# 若你使用 Django 開發版本,你可以指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50. 在以前的 Django 版本,沒有任何辦法改變 50 這個長度.
# 
# 這暗示了 db_index=True.
# 
# 它接受一個額外的參數: prepopulate_from, which is a list of fields from which to auto-populate the slug, via JavaScript, in the object's admin form:
# 
# models.SlugField(prepopulate_from=("pre_name", "name"))
# prepopulate_from 不接受 DateTimeFields.
# 
# admin 用一個``<input type="text">``表示 SlugField 字段數據(一個單行編輯框) 
# 
# SmallIntegerField
# 類似 IntegerField, 不過只允許某個取值范圍內的整數.(依賴數據庫)
#  
# TextField
# 一個容量很大的文本字段.
# 
# admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).
# 
# TimeField
# A time. Accepts the same auto-population options as DateField 和 DateTimeField.
# 
# admin 用一個 <input type="text"> 文本框表示該字段保存的數據(附加一些JavaScript shortcuts).
# 
# URLField
# 用於保存 URL. 若 verify_exists 參數為 True (默認), 給定的 URL 會預先檢查是否存在(即URL是否被有效裝入且沒有返回404響應).
# 
# admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)
# 
# USStateField
# 一個兩字母的美國州名縮寫.
# 
# admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)
# 
# XMLField
# 一個校驗值是否為合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema 的文件系統路徑.
View Code

4.2.1  模型類的定義(二)      

一  定義數據模型的擴展屬性

     通過內部類Meta給數據模型類增加擴展屬性:

     class Meta:

             verbose_name='名稱'      #表名由英文轉換成中文了

             verbose_name_plural='名稱復數形式'

             ordering='排序字段'   

    創建超級用戶后,登錄admin發現我們定義的表並不在,我們需要對所創建的表(類)進行注冊:

    

#admin.py
# Register your models here.
from django.contrib import admin
from app01.models import *
# @admin.register(Publisher)
class PubAdmin(admin.ModelAdmin):
    list_display = ("name","country","city")
    search_fields = ('name','city')
    list_filter = ('state_province',)
    ordering = ('name',)
    # fields = ('name','address',)
    # exclude = ('name','address')
    fieldsets = (
        (None,{'fields':('name',"address")}),
        ("Advanced options",{'classes':('collapse',),
                             'fields' :('city','country','website')

                             })
    )

admin.site.register(Author)
admin.site.register(AuthorDetail)
admin.site.register(Publisher,PubAdmin)
admin.site.register(Book)
對應代碼

 

這是因為:

           

     

二  定義模型方法

    定義模型方法和定義普通python類方法沒有太大的差別,定義模型方法可以將當前對應的數據組裝成具體的業務邏輯。

     示例:定義__str__()讓對象有一個名字

     def __str__(self):

           return self.name

當添加一個作者保存后:       

          這里只顯示生成了一個作者對象,可我們希望在這里出現的是作者的名字,所以:

         

刷新頁面:

4.3 ORM常用操作

4.3.1 增加

    create和save方法

實例:

>>> from app01.models import *
>>> Author.objects.create(name='Alvin')
<Author: Alvin>
>>> AuthorDetail.objects.create(sex=False,email='916852314@qq.com',address='bejing',birthday='1995-3-16',author_id=1)
<AuthorDetail: AuthorDetail object>
>>> pub=Publisher()
>>> pub.name='河大出版社'
>>> pub.address='保定'
>>> pub.city='保定'
>>> pub.state_province='河北'
>>> pub.country='China'
>>> pub.website='http://www.beida.com'
>>> pub.save()
對應代碼

注意:如果每次創建一個對象,想顯示對應的raw sql,需要在settings加上日志記錄部分:

LOGGING = {
#     'version': 1,
#     'disable_existing_loggers': False,
#     'handlers': {
#         'console':{
#             'level':'DEBUG',
#             'class':'logging.StreamHandler',
#         },
#     },
#     'loggers': {
#         'django.db.backends': {
#             'handlers': ['console'],
#             'propagate': True,
#             'level':'DEBUG',
#         },
#     }
# }
View Code

那么如何插入存在外鍵和多對多關系的一本書的信息呢?

>>> Book.objects.create(title='php',publisher=pub,publication_date='2017-7-7')
<Book: php>
>>> author1=Author.objects.get(id=1)
>>> author2=Author.objects.get(name='alvin')
>>> book=Book.objects.get(id=1)
>>> book.authors.add(author1,author1)
>>> book.authors.add(author1,author2)
對應代碼

所有表結果如下:

          

     

     

     

 總結:

      1   objects:   model默認管理器。

      2   插入主外鍵關系的時候,可以用對象的方式,也可以用關聯id的方式。

      3   插入多對多關系的時候要分步操作。

      4   create是管理器objects的方法

           save是model對象的方法

4.3.2 修改

    update和save方法

實例:

    

>>> author=Author.objects.get(id=2)
>>> author.name='tenglan'
>>> author.save()
>>> Publisher.objects.filter(id=2).update(name='American publisher')
對應代碼

注意:<1>不能用get的原因是:update是QuerySet對象的方法,get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象。

         <2>  filter:

>>> Publisher.objects.filter(name__contains="press")
[   <Publisher: Apress>]

 在 name 和 contains 之間有雙下划線。和Python一樣,Django也使用雙下划線來表明會進行一些魔術般的操作。這里,contains部分會被Django翻譯成LIKE語句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE '%press%';

其他的一些查找類型有:icontains(大小寫不敏感的LIKE),startswithendswith, 還有range     

     <3> 在“插入和更新數據”小節中,我們有提到模型的save()方法,這個方法會更新一行里的所有列。 而某些情況下,我們只需要更新行里的某幾列。例如說我們現在想要將Apress Publisher的名稱由原來的”Apress”更改為”Apress Publishing”。若使用save()方法,如:

>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()

  這等同於如下SQL語句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
 
UPDATE books_publisher SET
    name = 'Apress Publishing',
    address = '2855 Telegraph Ave.',
    city = 'Berkeley',
    state_province = 'CA',
    country = 'U.S.A.',
    website = 'http://www.apress.com'
WHERE id = 52;

  注意在這里我們假設Apress的ID為52)

在這個例子里我們可以看到Django的save()方法更新了不僅僅是name列的值,還有更新了所有的列。 若name以外的列有可能會被其他的進程所改動的情況下,只更改name列顯然是更加明智的。 更改某一指定的列,我們可以調用結果集(QuerySet)對象的update()方法: 示例如下:

>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')

  與之等同的SQL語句變得更高效,並且不會引起競態條件。

UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;

  update()方法對於任何結果集(QuerySet)均有效,這意味着你可以同時更新多條記錄。 以下示例演示如何將所有Publisher的country字段值由’U.S.A’更改為’USA’:

>>> Publisher.objects.all().update(country='USA')
2

  update()方法會返回一個整型數值,表示受影響的記錄條數。 在上面的例子中,這個值是2。

4.3.3  查詢 

>>> Publisher.objects.all()
[<Publisher: 中國機械出版社>, <Publisher: American publisher>]

注意:

這相當於這個SQL語句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher;

注意到Django在選擇所有數據時並沒有使用 SELECT* ,而是顯式列出了所有字段。

惰性機制:

所謂惰性機制:Publisher.objects.all()只是返回了一個QuerySet(查詢結果集對象),並不會馬上執行sql,而是當調用QuerySet的時候才執行。

QuerySet特點:

       1   可迭代的

       2   可切片

一  查詢相關API:

 <1>get(**kwargs):        返回與所給篩選條件相匹配的對象,返回結果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。

 <2>all():                       查詢所有結果

 <3>filter(**kwargs):      它包含了與所給篩選條件相匹配的對象

 <4>exclude(**kwargs):  它包含了與所給篩選條件不匹配的對象

 <5>order_by(*field):      對查詢結果排序

 <6>reverse():                對查詢結果反向排序

 <7>distinct():                從返回結果中剔除重復紀錄

 <8>values(*field):         返回一個ValueQuerySet——一個特殊的QuerySet,運行后得到的並不是一

                                     系列 model的實例化對象,而是一個可迭代的字典序列

 <9>values_list(*field):   它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列

 <10>count():                返回數據庫中匹配查詢(QuerySet)的對象數量。

<11>first():                   返回第一條記錄,等價於[:1][0]

<12>last():                   返回最后一條記錄,等價於[::1][0]

 <13>exists():               如果QuerySet包含數據,就返回True,否則返回False。

實例:

      1   查詢id為2的書籍信息,並只顯示書籍名稱和出版日期           

>>> Book.objects.filter(id=2).values("title","publication_date")
        [{'title': 'python Gone', 'publication_date': datetime.date(2019, 5, 24)}]
View Code

      2   查詢所有的出版信息,並按id降序排列,並嘗試使用reverse方法進行反向排序。          

>>> Publisher.objects.all().order_by("id")
        [<Publisher: 中國機械出版社>, <Publisher: Boo>, <Publisher: 人民出版社>]
>>> Publisher.objects.all().order_by("id").reverse()
        [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中國機械出版社>]                 

>>> Publisher.objects.all().order_by("-id")
        [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中國機械出版社>] 
View Code

     3    查詢出版社所在的城市信息,城市信息不要重復                

>>> Publisher.objects.all().values("city").distinct()
        [{'city': '北京'}, {'city': 'beijing'}]
View Code

      4    查詢城市是北京的出版社,嘗試使用exclude方法            

>>> Publisher.objects.all().filter(city='beijing')
        [<Publisher: Boo>]
View Code

      5    查詢男作者的數量            

>>> AuthorDetail.objects.filter(sex=0).count()
       1
View Code

 

二 多表關聯查詢

           1   外鍵關聯查詢

                  >>> AuthorDetail.objects.all().values("author")

        [{'author': 1}]
>>> AuthorDetail.objects.all().values("author__name")
        [{'author__name': 'alex'}]

2  多對多關聯查詢

      >>> Book.objects.all().filter(title='python Gone').values("authors")

        [{'authors': 1}, {'authors': 2}]
>>> Book.objects.all().filter(title='python Gone').values("authors__name")
        [{'authors__name': 'alex'}, {'authors__name': 'alvin'}]

>>> Book.objects.all().filter(authors__name='alex').values('title')
        [{'title': 'python Gone'}]

多表查詢技巧:

       __:兩個下划線可以生成連接查詢,查詢關聯的字段信息

      _set:提供了對象訪問相關聯表數據的方法。但是這種方法只能是相關類訪問定義了關系的類(主鍵類訪問外鍵類)

三 聚合查詢和分組查詢

    1  annotate(*args,**kwargs):可以為QuerySet每一個對象添加注解。可以通過計算查詢結果中每一個對象所關聯的對象集合,從而得出總計值(也可以是平均值或總和),用於分組查詢

    2  aggregate(*args,**kwargs):通過對QuerySet進行計算,返回一個聚合值的字典。

aggregate()中每一個參數都指定一個包含在字典中的返回值。用於聚合查詢。

聚合函數(Aggregation Functions)

所在位置:django.db.models

1 Avg:  返回所給字段的平均值

2 Count: 根據所給的關聯字段返回被關聯的model的數量

3 Max:返回所給字段的最大值

4 Sum:計算所給字段的總和

  為了演示實例,在這里我們給Book增加一個新的price字段:

       

  然后同步數據庫:makemigrations, migrate就可以了,這也是django牛逼之處。

  另外,創建一些新的書:

        

  注意,在pycharm的database下創建新的對象,有個bug:

        

   我現在添入了第六條數據,可是,左上角的row仍然顯示5,如果refresh:

       

   點擊yes,添加的數據則會消失。

    解決方法:當你創建完一條數據后,點擊+按鈕,row變成6了,數據自動就提交了,然后再把新創建的空行刪除(-)就可以了!

    Book對象添加完,還差一個字段:authors,綁定多對多的關系:

      

    插曲結束,轉入正題:

           python manage.py shell(重新打開),因為添加了新的字段

      

           實例:

            1   查詢中國郵電大學出版社出版了多少本書?           

       

            2   查詢alex出的書總價格                   

       

            3   查詢各個作者出的書的總價格

                         這里就涉及到分組了,分組條件是authors__name,

            

            4   查詢各個出版社最便宜的書價是多少

       

4.3.4  刪除

      >>> Book.objects.filter(id=1).delete()

       (3, {'app01.Book_authors': 2, 'app01.Book': 1})

       我們表面上刪除了一條信息,實際卻刪除了三條,因為我們刪除的這本書在Book_authors表中有兩條相關信息,這種刪除方式就是django默認的級聯刪除。

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM