Django適當進階篇

本節內容

學員管理系統練習

Django ORM操作進階

用戶認證

 

 

 

 

Django練習小項目:學員管理系統設計開發

帶着項目需求學習是最有趣和效率最高的,今天就來基於下面的需求來繼續學習Django 

項目需求:

1.分講師\學員\課程顧問角色,
2.學員可以屬於多個班級,學員成績按課程分別統計
3.每個班級至少包含一個或多個講師
4.一個學員要有狀態轉化的過程 ,比如未報名前,報名後,畢業老學員
5.客戶要有諮詢紀錄, 後續的定期跟蹤紀錄也要保存
6.每個學員的所有上課出勤情況\學習成績都要保存
7.學校可以有分校區,默認每個校區的員工只能查看和管理自己校區的學員
8.客戶諮詢要區分來源
#_*_coding:utf-8_*_
from django.db import models

# Create your models here.
from django.core.exceptions import ValidationError

from django.db import models
from django.contrib.auth.models import User

class_type_choices= (('online',u'網絡班'),
                     ('offline_weekend',u'面授班(週末)',),
                     ('offline_fulltime',u'面授班(脫產)',),
                     )
class UserProfile(models.Model):
    user = models.OneToOneField(User)
    name = models.CharField(u"姓名",max_length=32)
    def __unicode__(self):
        return self.name


class School(models.Model):
    name = models.CharField(u"校區名稱",max_length=64,unique=True)
    addr = models.CharField(u"地址",max_length=128)
    staffs = models.ManyToManyField('UserProfile',blank=True)
    def __unicode__(self):
        return self.name


class Course(models.Model):
    name = models.CharField(u"課程名稱",max_length=128,unique=True)
    price = models.IntegerField(u"面授價格")
    online_price = models.IntegerField(u"網絡班價格")
    brief = models.TextField(u"課程簡介")
    def __unicode__(self):
        return self.name


class ClassList(models.Model):
    course = models.ForeignKey('Course')
    course_type = models.CharField(u"課程類型",choices=class_type_choices,max_length=32)
    semester = models.IntegerField(u"學期")
    start_date = models.DateField(u"開班日期")
    graduate_date = models.DateField(u"結業日期",blank=True,null=True)
    teachers = models.ManyToManyField(UserProfile,verbose_name=u"講師")


    #def __unicode__(self):
    #    return "%s(%s)" %(self.course.name,self.course_type)

    class Meta:
        verbose_name = u'班級列表'
        verbose_name_plural = u"班級列表"
        unique_together = ("course","course_type","semester")


class Customer(models.Model):
    qq = models.CharField(u"QQ號",max_length=64,unique=True)
    name = models.CharField(u"姓名",max_length=32,blank=True,null=True)
    phone = models.BigIntegerField(u'手機號',blank=True,null=True)
    stu_id = models.CharField(u"學號",blank=True,null=True,max_length=64)
    #id = models.CharField(u"身份證號",blank=True,null=True,max_length=128)
    source_type = (('qq',u"qq羣"),
                   ('referral',u"內部轉介紹"),
                   ('51cto',u"51cto"),
                   ('agent',u"招生代理"),
                   ('others',u"其它"),
                   )
    source = models.CharField(u'客戶來源',max_length=64, choices=source_type,default='qq')
    referral_from = models.ForeignKey('self',verbose_name=u"轉介紹自學員",help_text=u"若此客戶是轉介紹自內部學員,請在此處選擇內部學員姓名",blank=True,null=True,related_name="internal_referral")

    course = models.ForeignKey(Course,verbose_name=u"諮詢課程")
    class_type = models.CharField(u"班級類型",max_length=64,choices=class_type_choices)
    customer_note = models.TextField(u"客戶諮詢內容詳情",help_text=u"客戶諮詢的大概情況,客戶個人信息備註等...")
    status_choices = (('signed',u"已報名"),
                      ('unregistered',u"未報名"),
                      ('graduated',u"已畢業"),
                      )

    status = models.CharField(u"狀態",choices=status_choices,max_length=64,default=u"unregistered",help_text=u"選擇客戶此時的狀態")
    consultant = models.ForeignKey(UserProfile,verbose_name=u"課程顧問")
    date = models.DateField(u"諮詢日期",auto_now_add=True)

    class_list = models.ManyToManyField('ClassList',verbose_name=u"已報班級",blank=True)

    def __unicode__(self):
        return "%s,%s" %(self.qq,self.name )



class ConsultRecord(models.Model):
    customer = models.ForeignKey(Customer,verbose_name=u"所諮詢客戶")
    note = models.TextField(u"跟進內容...")
    status_choices = ((1,u"近期無報名計劃"),
                      (2,u"2個月內報名"),
                      (3,u"1個月內報名"),
                      (4,u"2周內報名"),
                      (5,u"1周內報名"),
                      (6,u"2天內報名"),
                      (7,u"已報名"),
                      )
    status = models.IntegerField(u"狀態",choices=status_choices,help_text=u"選擇客戶此時的狀態")

    consultant = models.ForeignKey(UserProfile,verbose_name=u"跟蹤人")
    date = models.DateField(u"跟進日期",auto_now_add=True)

    def __unicode__(self):
        return u"%s, %s" %(self.customer,self.status)

    class Meta:
        verbose_name = u'客戶諮詢跟進記錄'
        verbose_name_plural = u"客戶諮詢跟進記錄"



class CourseRecord(models.Model):
    course = models.ForeignKey(ClassList,verbose_name=u"班級(課程)")
    day_num = models.IntegerField(u"節次",help_text=u"此處填寫第幾節課或第幾天課程...,必須爲數字")
    date = models.DateField(auto_now_add=True,verbose_name=u"上課日期")
    teacher = models.ForeignKey(UserProfile,verbose_name=u"講師")
    def __unicode__(self):
        return u"%s 第%s天" %(self.course,self.day_num)
    class Meta:
        verbose_name = u'上課紀錄'
        verbose_name_plural = u"上課紀錄"
        unique_together = ('course','day_num')


class StudyRecord(models.Model):
    course_record = models.ForeignKey(CourseRecord, verbose_name=u"第幾天課程")
    student = models.ForeignKey(Customer,verbose_name=u"學員")
    record_choices = (('checked', u"已簽到"),
                      ('late',u"遲到"),
                      ('noshow',u"缺勤"),
                      ('leave_early',u"早退"),
                      )
    record = models.CharField(u"上課紀錄",choices=record_choices,default="checked",max_length=64)
    score_choices = ((100, 'A+'),
                     (90,'A'),
                     (85,'B+'),
                     (80,'B'),
                     (70,'B-'),
                     (60,'C+'),
                     (50,'C'),
                     (40,'C-'),
                     (0,'D'),
                     (-1,'N/A'),
                     (-100,'COPY'),
                     (-1000,'FAIL'),
                     )
    score = models.IntegerField(u"本節成績",choices=score_choices,default=-1)
    date = models.DateTimeField(auto_now_add=True)
    note = models.CharField(u"備註",max_length=255,blank=True,null=True)

    def __unicode__(self):
        return u"%s,學員:%s,紀錄:%s, 成績:%s" %(self.course_record,self.student.name,self.record,self.get_score_display())

    class Meta:
        verbose_name = u'學員學習紀錄'
        verbose_name_plural = u"學員學習紀錄"
        unique_together = ('course_record','student')
學員管理系統表結構

 

常用ORM操作

from django.db import models

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

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):              # __unicode__ on Python 2
        return self.headline
示例models

 

創建

1
2
3
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

This performs an INSERT SQL statement behind the scenes. Django doesn’t hit the database until you explicitly call save().

The save() method has no return value.

處理帶外鍵關聯或多對多關聯的對象 

 

ForeignKey的關聯

1
2
3
4
5
>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

ManyToManyField關聯  

1
2
3
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

添加多個ManyToMany對象

1
2
3
4
5
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

 

查詢

all_entries = Entry.objects.all() #查詢所有
Entry.objects.filter(pub_date__year=2006) #查詢所有pub_date爲2006年的紀錄
Entry.objects.all().filter(pub_date__year=2006) #與上面那句一樣
>>> Entry.objects.filter(   #鏈式查詢
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )

one_entry = Entry.objects.get(pk=1) #單條查詢

Entry.objects.all()[:5] #查詢前5條
Entry.objects.all()[5:10] #你猜

Entry.objects.order_by('headline')[0] #按headline排序取第一條

Entry.objects.filter(pub_date__lte='2006-01-01') #相當於sql語句SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

Entry.objects.get(headline__exact="Cat bites dog") #相當於SELECT ... WHERE headline = 'Cat bites dog';
Blog.objects.get(name__iexact="beatles blog") #與上面相同,只是大小寫不敏感

Entry.objects.get(headline__contains='Lennon') #相當 於SELECT ... WHERE headline LIKE '%Lennon%';
單表內查詢語句
#This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':
Entry.objects.filter(blog__name='Beatles Blog')

Blog.objects.filter(entry__headline__contains='Lennon')
關聯查詢

對同一表內不同的字段進行對比查詢,In the examples given so far, we have constructed filters that compare the value of a model field with a constant. But what if you want to compare the value of a model field with another field on the same model?

Django provides expressions to allow such comparisons. Instances of F() act as a reference to a model field within a query. These references can then be used in query filters to compare the values of two different fields on the same model instance.

For example, to find a list of all blog entries that have had more comments than pingbacks, we construct an F() object to reference the pingback count, and use that F() object in the query:

1
2
>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

Django supports the use of addition, subtraction, multiplication, division, modulo, and power arithmetic with F() objects, both with constants and with other F() objects. To find all the blog entries with more than twice as many comments as pingbacks, we modify the query:

1
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'* 2)

To find all the entries where the rating of the entry is less than the sum of the pingback count and comment count, we would issue the query:

1
>>> Entry.objects.filter(rating__lt=F('n_comments'+ F('n_pingbacks'))

For date and date/time fields, you can add or subtract a timedelta object. The following would return all entries that were modified more than 3 days after they were published:

1
2
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date'+ timedelta(days=3))

Caching and QuerySets

Each QuerySet contains a cache to minimize database access. Understanding how it works will allow you to write the most efficient code.

In a newly created QuerySet, the cache is empty. The first time a QuerySet is evaluated – and, hence, a database query happens – Django saves the query results in the QuerySet’s cache and returns the results that have been explicitly requested (e.g., the next element, if the QuerySet is being iterated over). Subsequent evaluations of the QuerySet reuse the cached results.

Keep this caching behavior in mind, because it may bite you if you don’t use your QuerySets correctly. For example, the following will create two QuerySets, evaluate them, and throw them away:

1
2
>>> print([e.headline for in Entry.objects.all()])
>>> print([e.pub_date for in Entry.objects.all()])

That means the same database query will be executed twice, effectively doubling your database load. Also, there’s a possibility the two lists may not include the same database records, because an Entry may have been added or deleted in the split second between the two requests.

To avoid this problem, simply save the QuerySet and reuse it:

1
2
3
>>> queryset = Entry.objects.all()
>>> print([p.headline for in queryset]) # Evaluate the query set.
>>> print([p.pub_date for in queryset]) # Re-use the cache from the evaluation.

When QuerySets are not cached

Querysets do not always cache their results. When evaluating only part of the queryset, the cache is checked, but if it is not populated then the items returned by the subsequent query are not cached. Specifically, this means that limiting the querysetusing an array slice or an index will not populate the cache.

For example, repeatedly getting a certain index in a queryset object will query the database each time:

1
2
3
>>> queryset = Entry.objects.all()
>>> print queryset[5# Queries the database
>>> print queryset[5# Queries the database again

However, if the entire queryset has already been evaluated, the cache will be checked instead:

1
2
3
4
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print queryset[5# Uses cache
>>> print queryset[5# Uses cache

 

Complex lookups with Q objects(複雜查詢)

Keyword argument queries – in filter(), etc. – are “AND”ed together. If you need to execute more complex queries (for example, queries with OR statements), you can use objects.

object (django.db.models.Q) is an object used to encapsulate a collection of keyword arguments. These keyword arguments are specified as in “Field lookups” above.

For example, this Q object encapsulates a single LIKE query:

1
2
from django.db.models import Q
Q(question__startswith='What')

Q objects can be combined using the & and | operators. When an operator is used on two Q objects, it yields a new Q object.

For example, this statement yields a single Q object that represents the “OR” of two "question__startswith" queries:

1
Q(question__startswith='Who') | Q(question__startswith='What')

This is equivalent to the following SQL WHERE clause:

1
WHERE question LIKE 'Who%' OR question LIKE 'What%'

You can compose statements of arbitrary complexity by combining Q objects with the & and | operators and use parenthetical grouping. Also, Q objects can be negated using the ~ operator, allowing for combined lookups that combine both a normal query and a negated (NOT) query:

1
Q(question__startswith='Who') | ~Q(pub_date__year=2005)

Each lookup function that takes keyword-arguments (e.g. filter()exclude()get()) can also be passed one or more Qobjects as positional (not-named) arguments. If you provide multiple Q object arguments to a lookup function, the arguments will be “AND”ed together. For example:

1
2
3
4
Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(200552)) | Q(pub_date=date(200556))
)

... roughly translates into the SQL:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

Lookup functions can mix the use of Q objects and keyword arguments. All arguments provided to a lookup function (be they keyword arguments or Q objects) are “AND”ed together. However, if a Q object is provided, it must precede the definition of any keyword arguments. For example:

1
2
3
Poll.objects.get(
    Q(pub_date=date(200552)) | Q(pub_date=date(200556)),
    question__startswith='Who')

... would be a valid query, equivalent to the previous example; but:

1
2
3
4
# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(200552)) | Q(pub_date=date(200556)))

... would not be valid.

  

更新 

Updating multiple objects at once

1
2
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

在原有數據的基礎上批量自增

Calls to update can also use expressions to update one field based on the value of another field in the model. This is especially useful for incrementing counters based upon their current value. For example, to increment the pingback count for every entry in the blog:

1
>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks'+ 1)

However, unlike F() objects in filter and exclude clauses, you can’t introduce joins when you use F() objects in an update – you can only reference fields local to the model being updated. If you attempt to introduce a join with an F() object, a FieldErrorwill be raised:

1
2
# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))

 

 

Aggregation(聚合)

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

class Publisher(models.Model):
    name = models.CharField(max_length=300)
    num_awards = models.IntegerField()

class Book(models.Model):
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    rating = models.FloatField()
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    pubdate = models.DateField()

class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)
    registered_users = models.PositiveIntegerField()
示例models
# Total number of books.
>>> Book.objects.count()

# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()

# Average price across all books.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}

# Cost per page
>>> Book.objects.all().aggregate(
...    price_per_page=Sum(F('price')/F('pages'), output_field=FloatField()))
{'price_per_page': 0.4470664529184653}

# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.

# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
[<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]
>>> pubs[0].num_books

# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
常用聚合場景需求

更多聚合查詢例子:https://docs.djangoproject.com/en/1.9/topics/db/aggregation/ 

 

用戶認證 

 
1
2
3
4
5
6
7
8
9
10
11
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
    # the password verified for the user
    if user.is_active:
        print("User is valid, active and authenticated")
    else:
        print("The password is valid, but the account has been disabled!")
else:
    # the authentication system was unable to verify the username and password
    print("The username and password were incorrect.")

How to log a user out

1
2
3
4
5
from django.contrib.auth import logout
 
def logout_view(request):
    logout(request)
    # Redirect to a success page.

  

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章