Django 进阶(装饰器,Mixin,信号,模式)

来源:互联网 发布:unity3d 性能测试工具 编辑:程序博客网 时间:2024/05/16 15:03

Django中级用法

抽象models类

class BaseProfile(models.Model):    USER_TYPES = (        (0, 'Ordinary'),        (1, 'SuperHero'),    )    user = models.OnToOneField(settings.AUTH_USER_MODEL, primary_key=True)    user_type = models.IntegerField(max_length=1, null=True,    choices=USER_TYPES)    bio = models.CharField(max_length=200, blank=True, null=True)    def __str__(self):        return "{}:{:.20}".format(self.user, self.bio or "")    class Meta:        abstract = Trueclass SuperHeroProfile(models.Model):    origin = models.CharField(max_length=100, blank=True, null=True)    class Meta:        abstract = Trueclass OrdinaryProfile(models.Model):    address = models.CharField(max_length=200, blank=True, null=True)    class Meta:        abstract = Trueclass Profile(SuperHeroProfile, OrdinaryProfile, BaseProfile):    pass

扩展django内建User

定义(Profile.models)

# models.py# 将primary_key赋值为True,以阻止类似PostgreSQL这样的数据库后端中的并发问题class Profile(models.Model):    user = models.OnToOneField(settings.AUTH_USER_MODEL, primary_key=True)

信号

# signals.pyfrom django.db.models.signals import post_savefrom django.dispatch import receiverfrom django.conf import settingsfrom . import models@receiver(post_save, sender=settings.AUTH_USER_MODEL)def create_profile_handler(sender, instance, created, **kwargs):    if not created:        return    # 仅在created是最新时才创建账户对象    profile = models.Profile(user=instance)    profile.save()

首先,为你的应用创建一个__init__.py包以引用应用的ProfileConfig:

# __init__.pydefault_app_config = "profile.apps.ProfileConfig"

接下来是app.py中的子类ProfileConfig方法,可使用ready方法配置信号:

# apps.pyfrom django.apps import AppConfigclass ProfileConfig(AppConfig):    name = "profiles"    verbose_name = "User Profiles"    def ready(self):        from . import signals

为了操作方便,账户admin可以通过定义一个自定义的UserAdmin嵌入到默认的用户admin中:

# admin.pyfrom django.contrib import adminfrom .models import Profilefrom django.contrib.auth.models import Userclass UserProfileInline(admin.StackedInline):    model = Profileclass UserAdmin(admin.UserAdmin):    inlines = [UserProfileInline]admin.site.unregister(User)admin.site.register(User, UserAdmin)

更新:

上下文Mixin

class FeedMixin(object):    def get_context_data(self, **kwargs):        context = super().get_context_data(**kwargs)        context["feed"] = models.Post.objects.viewable_posts(self.request.user)        return context

注意双星号包含的变量

class MyFeed(**FeedMixin**, generic.CreateView):    model = models.Post    template_name = "myfeed.html"    success_url = reverse_lazy("my_feed")

服务模式

# service.pyAPI_URL = "http://api.herocheck.com/?q={0}"class SuperHeroWebAPI:    ...    @staticmehtod    def is_hero(username):        url = API_URL.format(username)        return webclient.get(url)

加上黑名单功能的服务

class SuperHeroWebAPI:    ...    @staticmethod    def is_hero(username):        blacklist = set(["syndrome", "kcka$$", "superfake"])        ulr = API_URL.format(username)        return username not in blacklist and webclient.get(url)

使用服务

from .services import SuperHeroWebAPIdef is_superhero(self):    return SuperHeroWebAPI.is_superhero(self.user.username)

模型的方法作为属性

Python类可以使用property装饰器把函数当作一个属性来使用。这样,Django模型也可以较好地利用它。替换前面那个例子中的函数:

@propertydef age(self):    ...

现在我们可以用profile.age来访问用户的年龄。注意,函数的名称要尽可能的短。

缓存特性

很好理解,直接看代码:

from django.utils.function import cached_property    #...    @cached_property    def full_name(self):        # 代价高昂的操作,比如,外部服务调用        return "{0} {1}".format(self.firstname, self.lastname)

定制模型管理器(Manager)

太简单了,直接看官网

Querysets 组合查询

from django.db.models import Q# Union 交集>>> User.objects.filter(Q(username__in["a", "b", "c"]) | Q(username__in=["c", "d"]))[`<User: a>, <User: b>, <User: c>, <User: d>`]# Intersection 并集>>> User.objects.filter(Q(username__in["a", "b", "c"]) & Q(username__in=["c", "d"]))[<User: c>]# Difference 补集>>> User.objects.filter(Q(username__in=["a", "b", "c"]) & ~Q(username__in=["c", "d"]))[<User: a>, <User: b>]

Querysets链接

这是一个很天真的做法,开销很大:

>>> recent = list(posts)+list(comments)>>> sorted(recent, key=lambda e: e.modified, reverse=True)[:3][<Post: user: Post1>, <Comment: user: Comment1>, <Post: user: Post0>]

一个更好的解决方案是使用迭代器减少内存消耗。如下,使用itertools.chain方法合并多个QuerySets:

>>> from itertools import chain>>> recent = chain(posts, comments)>>> sorted(recent, key=lambda e: e.modified, reverse=True)[:3]

以上:2016年05月31日14:51:32

装饰器

第一种

@login_requireddef simple_view(request):       return HttpResponse()

2 通过对基于函数视图或者基于类视图使用一个装饰器实现控制:

@login_required(MyView.as_view())

3 通过覆盖mixin的类视图的dispatch方法实现控制:

class LoginRequiredMixin:    @method_decorator(login_required)    def dispatch(self, request, *args, **kwargs):        return super().dispatch(request, *args, **kwargs)

每个模板都要包含下列代码:

{% include "_navbar.html" with active_link='link2' %}

然后

{# _navbar.html #}<ul class="nav nav-pills">  <li{% if active_link == "link1" %} class="active"{% endif %}><ahref="{% url 'link1' %}">Link 1</a></li>  <li{% if active_link == "link2" %} class="active"{% endif %}><ahref="{% url 'link2' %}">Link 2</a></li>  <li{% if active_link == "link3" %} class="active"{% endif %}><ahref="{% url 'link3' %}">Link 3</a></li></ul>

之前我都是从view传一个current_page变量来判断的,好蠢

Template tag

# app/templatetags/nav.pyfrom django.core.urlresolvers import resolvefrom django.template import Libraryregister = Library()@register.simple_tagdef active_nav(request, url):   url_name = resolve(request.path).url_name   if url_name == url:       return "active"   return ""

使用:

{# base.html #}{% load nav %}<ul class="nav nav-pills">  <li class={% active_nav request 'active1' %}><a href="{% url'active1' %}">Active 1</a></li>  <li class={% active_nav request 'active2' %}><a href="{% url'active2' %}">Active 2</a></li>  <li class={% active_nav request 'active3' %}><a href="{% url'active3' %}">Active 3</a></li></ul>

表单

动态表单

# forms.pyclass PersonDetailsForm(forms.Form):    name = forms.CharField(max_length=100)    age = forms.IntegerField()    def __init__(self, *args, **kwargs):       upgrade = kwargs.pop("upgrade", False)       super().__init__(*args, **kwargs)       # Show first class option? 显示头等舱选项?       if upgrade:            self.fields["first_class"] = forms.BooleanField(label="Fly First Class?")
# views.pyclass PersonDetailsEdit(generic.FormView):    ...    def get_form_kwargs(self):        kwargs = super().get_form_kwargs()        kwargs["upgrade"] = True        return kwargs

用户表单(表单需要根据已经登录的用户来进行定制)

需要django-braces

from braces.forms import UserKwargModelFormMixinclass PersonDetailsForm(UserKwargModelFormMixin, forms.Form):    ...    def __init__(self, *args, **kwargs):       super().__init__(*args, **kwargs)       # Are you a member of the VIP group?       if self.user.groups.filter(name="VIP").exists():           self.fields["first_class"] = forms.BooleanField(label="Fly First Class?")

然后再确认只有登录用户才能看到此视图

class VIPCheckFormView(LoginRequiredMixin, UserFormKwargsMixin,generic.FormView):   form_class = PersonDetailsForm   ...

一个视图的多个表单行为

crispy表单订阅器修改不同的按钮

# forms.pyclass SubscribeForm(forms.Form):    email = forms.EmailField()    def __init__(self, *args, **kwargs):        super().__init__(*args, **kwargs)        self.helper = FormHelper(self)        self.helper.layout.append(Submit('subscribe_butn', 'Subscribe'))

UnSubscribeForm以完全相同的方式来定义,除了按钮不同

# views.pyfrom .forms import SubscribeForm, UnSubscribeFormclass NewsletterView(generic.TemplateView):   subcribe_form_class = SubscribeForm   unsubcribe_form_class = UnSubscribeForm   template_name = "newsletter.html"   def get(self, request, *args, **kwargs):       kwargs.setdefault("subscribe_form", self.subcribe_form_class())       kwargs.setdefault("unsubscribe_form", self.unsubcribe_form_class())       return super().get(request, *args, **kwargs)

单独来看post方法

    def post(self, request, *args, **kwargs):       form_args = {           'data': self.request.POST,           'files': self.request.FILES,       }       if "subscribe_butn" in request.POST:           form = self.subcribe_form_class(**form_args)           if not form.is_valid():               return self.get(request,                                  subscribe_form=form)           return redirect("success_form1")       elif "unsubscribe_butn" in request.POST:           form = self.unsubcribe_form_class(**form_args)           if not form.is_valid():               return self.get(request,                                  unsubscribe_form=form)           return redirect("success_form2")       return super().get(request)

这里我感觉登录和注册两个表单可以用这个方法


可以偷懒的第三方包^_^

package desc 例 django-braces 通用视图扩展(Mixin) StaticContextMixin django-crispy-forms form前端标签扩展 {% crispy_form %} zebra stripes are neat $1
1 0