`
差沙
  • 浏览: 19314 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

django的Model的“造Class”概述

阅读更多
在朋友和同事的极力推荐下最近开始看上了python,其实主要是还是因为python是2007年度语言,怎么的也要与时俱进呀.最近一路看来有些心得,希望能与大家分享,小弟其实也只接触不到一周的python,有说错的地方还望大家指出改正.

不打算从py的语法基础说起了,直接说说对django的心得:

接触django首先需要了解可能就是他那个model,建立一个model就什么都有了,这对于搞java得人员来说还是挺有吸引力的(当然貌似对于动态语言这都是小儿科),那么让我们先看一个model的例子:

偷懒了,直接拿django-admin里面的User出来了

class User(models.Model): 
    username = models.CharField(_('username'), maxlength=30, unique=True, validator_list=[validators.isAlphaNumeric])) 
    first_name = models.CharField(_('first name'), maxlength=30, blank=True) 
    last_name = models.CharField(_('last name'), maxlength=30, blank=True) 
    email = models.EmailField(_('e-mail address'), blank=True) 
    password = models.CharField(_('password'), maxlength=128)) 
    class Meta: 
        ordering = ('username',)


每个属性就是一个库表的字段,定义起来非常简单明了,models里面提供了很多种类的Field类似上面的EmailField。不同的Field有不同的设置,可以看相应的原来来了解相关的设置.

在model class内部还有一个class Meta,这个Class的属性制定了这个表的一些存取策略,例如这里的ordering。MetaClass里面的属性可以用model的_meta属性取得。OK,那么这样一个model怎么就能实现对数据库表的灵活操作了呢。让我们来看看吧。

首先先分析一下/django/django/db/models/base.py这个文件,其中包含了models.Model这类的定义:

看看class定义的第一行吧,第一行就够我琢磨一阵子的了:

class Model(object):
    __metaclass__ = ModelBase


Model采用了new style class定义,关于这个内容大家可以放狗看一下,第一行是一个__metaclass__属性的定义,该属性的值是ModelBase,这是一个类。 __metaclass__的意思是,指定一个class,这个class的实例就是本class,相信您已经晕了。那么就拿这个Model的例子来说明一下,如果没有__metaclass__这个属性,产生一个实例就是正常的流程,有了这个属性流程会有改变:

首先调用BaseModel.__new__(cls, name, bases, attrs)这个方法,回返回的值是一个class类型,然后用这个class来创建实例。其实BaseModel就是Model的元类,来制定 Model这个类的最终样子。关于元类的更多信息请看这里

那么我们的目光一下转移到BaseModel这个类上,我有种直觉,Meta这个class最后可以用_meta来取就是在这里做的手脚,看一下BaseModel的定义吧,有点长:

class ModelBase(type):
    "Metaclass for all models"
    def __new__(cls, name, bases, attrs):
        # If this isn't a subclass of Model, don't do anything special.

        if name == 'Model' or not filter(lambda b: issubclass(b, Model), bases):    #1
            return super(ModelBase, cls).__new__(cls, name, bases, attrs)

        # Create the class.

        new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')})    #2

        new_class.add_to_class('_meta', Options(attrs.pop('Meta', None)))     #3

        new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}))

        # Build complete list of parents                                      #4
        for base in bases:
            # TODO: Checking for the presence of '_meta' is hackish.
            if '_meta' in dir(base):
                new_class._meta.parents.append(base)
                new_class._meta.parents.extend(base._meta.parents)

        model_module = sys.modules[new_class.__module__]

        if getattr(new_class._meta, 'app_label', None) is None:
            # Figure out the app_label by looking one level up.
            # For 'django.contrib.sites.models', this would be 'sites'.
            new_class._meta.app_label = model_module.__name__.split('.')[-2]  #5

        # Bail out early if we have already created this class.
        m = get_model(new_class._meta.app_label, name, False)                 #6
        if m is not None:
            return m

        # Add all attributes to the class.
        for obj_name, obj in attrs.items():
            new_class.add_to_class(obj_name, obj)                             #7

        # Add Fields inherited from parents
        for parent in new_class._meta.parents:
            for field in parent._meta.fields:
                # Only add parent fields if they aren't defined for this class.
                try:
                    new_class._meta.get_field(field.name)
                except FieldDoesNotExist:
                    field.contribute_to_class(new_class, field.name)          #8

        new_class._prepare()

        register_models(new_class._meta.app_label, new_class)                 #9
        # Because of the way imports happen (recursively), we may or may not be
        # the first class for this model to register with the framework. There
        # should only be one class for each model, so we must always return the
        # registered version.
        return get_model(new_class._meta.app_label, name, False)              #10


简单分析一下这个代码:

1. 检查class是否为Model的子类,不是的话,不做任何处理,直接传给父类处理,也就相当于正常的处理了class,注意super在多重继承的时候应该严格使用

2. 用type来创建类,创建的就是正常的ModelClass

3. 这句很重要,add_to_class是Model里面的class方法,这个方法其实就是传入name和value,给Model添加class属性.看到了,原来神奇的_meta就是这么来的. 提到add_to_class方法,简单看一下它的代码:

    def add_to_class(cls, name, value):
        if name == 'Admin':
            assert type(value) == types.ClassType, "%r attribute of %s model must be a class, not a %s object" % (name, cls.__name__, type(value))
            value = AdminOptions(**dict([(k, v) for k, v in value.__dict__.items() if not k.startswith('_')]))
        if hasattr(value, 'contribute_to_class'):
            value.contribute_to_class(cls, name)
        else:
            setattr(cls, name, value)
    add_to_class = classmethod(add_to_class)


最后一句是制定这个方法是class方法,特点就是方法的第一个参数是本class,其实classmethod就是一个装饰器,在2。4之后可以使用@ 来简写。这里不得不提的是他对Admin的特殊处理,虽然AdminOption不是在admin模块里面的,但是这么做还是跟一个Admin的东东绑定起来了,在java的世界解耦是一件大事,看到下面还有对'contribute_to_class'这个方法的特殊处理,django为啥不弄的解耦点呢。而且同样是包装成Option,一个是在BaseModel里面弄(那个Meta的包装),一个在add_to_class方法里面弄,实在有点不优雅,可能还没了解太多,不知道他的深度用意吧。

4. Meta的集成,Option的这个类提供继承方法

5. 取得applabel,就是把model的名字分割取到数第二个,我很喜欢-2这样的设定

6. get_model方法取得缓存里面的东西。

7. 把所有的class attr拿出来搞一遍,一般的属性就setattr弄回去了,要是这个属性有contribute_to_class这个callable属性,那就执行之(Admin的处理完全也可以这样,其实我们常用的objects就是用这个方法弄的)

8. 每个Field调用自己的contribute_to_class方法来进行特殊的处理

9. 进入缓存,,暂且叫缓存吧,里面的东西大家看看很简单 文件在 /django/django/db/models/loading.py 里面还是有很多内容的

10.看注释说的很清楚了,我们一定要在缓存里面拿model。



其中需要指出的是,new_class._prepare() 这个方法,简单列出片段:

    def _prepare(cls):
        # Creates some methods once self._meta has been populated.
        opts = cls._meta
        opts._prepare(cls)

        ....

        dispatcher.send(signal=signals.class_prepared, sender=cls)


中间省略了一些代码,不是我没看懂的就是没意思的,关键要看这个dispatcher呀。这里是监听者模式,dispatcher.send(signal=signals.class_prepared, sender=cls)放松了一个包含特定信号的事件,让监听的人可以做相应的处理。这样的信号还有很多种,我们可以很简单的截获信号,处理相应的内容。也许您还记得,在我们创建db的时候,会提示创建一个超级用户,其实就是用这个方法来弄的。

那我为什么要把这个单独拿出来说呢,因为监听这个事件的人不是别人,是"objects"!!!在文件/django/django/db/models/manager.py的开头就有这样的代码:

def ensure_default_manager(sender):
    cls = sender
    if not hasattr(cls, '_default_manager'):
        # Create the default manager, if needed.
        try:
            cls._meta.get_field('objects')
            raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % cls.__name__
        except FieldDoesNotExist:
            pass
        cls.add_to_class('objects', Manager())

dispatcher.connect(ensure_default_manager, signal=signals.class_prepared)


定义了一个callable,然后监听signals.class_prepared信号,呵呵,连上了吧,只要Class准备好了,就调用这个方法。简单判断有没有_default_manager'属性和meta里面的objects后,开始插入objects。 cls.add_to_class('objects', Manager()) 很熟悉吧,前面讲过不多说了。

PS:写到这里不由得感叹,为啥同样是往Class里面加入东东,要搞这么多的花样呢,我以前也写过一个Rails配置加载过程的分析文档,虽然一个简单的加载配置就把所有动态语言玩个遍,但是这也太不规范了吧,可能不这么玩就不算“动态”语言了吧,哈哈。

终于Model的Class造好了,相信大家以后造自己的Class也能玩出更多的花样。那么可以开始下一步了。我到老家后再写吧,打算讲讲神奇的objects,也就是Manager,其实就是玩QuerySet。。那里有很多的__xxx__方法,很好很强大
分享到:
评论
3 楼 huangyiiiiii 2008-04-22  
You was reading django 0.96, right?
A lot of refactoring have been done since then.
2 楼 差沙 2008-03-06  
py web开发的话,还是看好djnago
1 楼 fengzl 2008-03-06  
django这东西还是不温不火

相关推荐

    Python Django Model模型

    class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() pub_date = models.DateTimeField('date published') def __str__(self): return self.title ``` 在这...

    Django1.9 官方文档

    开发者可以了解到模型的基础知识、字段类型(Fieldtypes)、元选项(Metaoptions)以及模型类(Modelclass)等。在查询集(QuerySets)部分,介绍了如何执行查询、查询集方法的引用以及查找表达式。而在模型实例...

    Django2.1官方文档

    1. 文档概述 文档首先介绍如何获取帮助(Getting help)以及文档的组织结构(How the documentation is organized),确保读者能够快速找到所需的信息。 2. 安全性(Security) 该部分着重讲解Django框架中的安全性...

    Django-2.1.15.tar.gz

    class Blog(models.Model): title = models.CharField(max_length=200) pub_date = models.DateTimeField() ``` 6. **URL路由** Django使用URLconfs进行URL到视图的映射。在项目的urls.py和应用的urls.py中...

    django1.11.1

    ### Django 1.11.1 版本概述与核心知识点解析 #### 一、Django 1.11.1 版本介绍 Django 1.11.1 是一个重要的版本更新,发布于2017年4月5日。此版本对之前的Django 1.11 进行了一系列的修复和完善,提供了更稳定和...

    Django docs-1.11-en

    ### Django 1.11 文档概述与关键知识点解析 #### 一、文档概览 **标题**: Django docs-1.11-en **描述**: Django 官方文档,最新出炉 1.11 版本。 **标签**: Django **部分内容**: 该文档覆盖了 Django 1.11 的...

    django 实现手动存储文件到model的FileField

    ### Django 手动存储文件至 Model 的 FileField 在Django框架中,处理文件上传是一项常见且重要的功能。本文档将详细介绍如何手动地将文件存储到Model中的`FileField`,并通过具体示例来帮助理解整个过程。 #### ...

    django基础

    一、Django 概述 Django 提供了 MVC(Model-View-Controller)架构的变种——MTV(Model-Template-View),使得开发者可以高效地创建功能丰富的 Web 应用。它包含以下几个核心组件: 1. **模型 (Model)**:负责...

    Django框架讲义.zip

    **一、Django框架概述** Django基于模型-视图-控制器(MVC)设计模式,但在Django中通常称为模型-视图-模板(MVT)。它的主要组件包括: 1. **模型(Model)**:用于处理数据库操作,定义数据结构和业务逻辑。 2. ...

    django model的update时auto_now不被更新的原因及解决方式

    ### Django Model Update时Auto_now不被更新的原因及解决方式 #### 一、问题背景与概述 在使用Django框架进行Web开发时,我们经常会遇到需要更新数据库中的某些记录的情况。为了方便跟踪这些记录的更新时间,...

    Django2 文档

    以上只是Django 2.0框架部分核心知识点的概述,实际开发中还会涉及到更多的特性和最佳实践,如测试、部署、第三方库集成等。深入学习Django,开发者可以构建出高效、安全、易于维护的Web应用程序。

    Python Django基础教程(全教程)

    这只是Django基础教程的概述,实际开发中还包括错误处理、中间件、缓存、用户认证、国际化等多个方面。深入学习Django,你将能构建功能强大的Web应用程序。对于初学者来说,Django官方文档是很好的学习资源,同时...

    django创建新闻文章的操作步骤.doc

    class Category(models.Model): title = models.CharField(max_length=20, verbose_name='名称') class Meta: ordering = ['title'] verbose_name = '新闻类别' verbose_name_plural = verbose_name def __...

    PyPI 官网下载 | django-image-cropping-0.4.0.tar.gz

    **一、django-image-cropping概述** `django-image-cropping`库主要解决的是在Django项目中处理用户上传图片时,如何实现自由裁剪以适应不同场景的需求。它允许用户在前端选择感兴趣的图像区域,然后在后端进行精确...

    django book 中文版

    - **Django** 是一个用Python编写的开源Web框架,遵循MVC(Model-View-Controller)设计模式,但更准确地来说它采用的是MTV(Model-Template-View)架构。 - **目标与特点**:Django旨在简化Web开发过程,提供了一个...

    django入门项目源码含数据库,一个简单的django项目

    1. **Django框架概述** Django提供了全面的内置功能,如ORM(对象关系映射)、模板系统、URL路由、身份验证和授权、表单处理等。其设计哲学是“快速开发、少写代码”,这使得开发者能够高效地创建功能丰富的Web应用...

    django xadmin action兼容自定义model权限教程

    ### Django Xadmin Action 兼容自定义Model权限教程 #### 背景介绍 Django Xadmin 是基于 Django 的一个非常强大的管理插件框架,它提供了丰富的功能来增强 Django admin 的用户体验与管理效率。在实际项目开发过程...

    Django官方文档,20121114发布

    - **Class-based views**:类视图的使用,包括概述、内置展示视图、内置编辑视图、混入类的使用以及API参考。 - **Advanced**:高级主题,如生成CSV和PDF文件。 - **Middleware**:中间件的概述及内置中间件类。 ##...

Global site tag (gtag.js) - Google Analytics