django 不包括字段 序列化器_手写一个Django序列化功能
生活随笔
收集整理的這篇文章主要介紹了
django 不包括字段 序列化器_手写一个Django序列化功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文章的代碼已上傳至github上(github包含了更多功能,相關文章后續更新)
AGL1994/django-building?github.com前言
目前Django比較知名的序列化框架有DRF,還有Django自帶的model_to_dict函數。DRF序列化需要 為model寫一個serializers類,同時需要配置相關的序列化字段,如果有外鍵關聯等,也需要作出相 關的配置。如果一個項目從0開始的話,還是建議使用DRF。對于Django自帶的model_to_dict函數, 還是有一些缺點,不能序列化Foreign的值,或者某些時間格式會報錯等。下面我們就進入正題。本例 子會使用反射相關的功能,也需要對Django框架有一個初步的了解。不清楚的童鞋可以先自行了解一下, 網上相關的帖子很多,這里就不多做講解。model
class User(AbstractUser):phone = models.CharField(max_length=11, blank=True, null=True, verbose_name="電話號碼")name = models.CharField(max_length=45, blank=True, null=True, verbose_name="姓名")sex = models.IntegerField(blank=True, null=True, choices=((1, "男"), (2, '女')), default=1)birthday = models.DateField(blank=True, null=True, verbose_name="出生日期")avatar = models.CharField(max_length=255, default=None, blank=True, null=True, verbose_name="用戶頭像")status = models.SmallIntegerField(default=1, choices=USER_STATUS, verbose_name="狀態")created = models.DateTimeField(verbose_name=u"創建時間", editable=False, auto_now_add=True)updated = models.DateTimeField(verbose_name=u"修改時間", editable=False, auto_now=True)deleted = models.SmallIntegerField(default=0)class Meta:db_table = 'tb_user'verbose_name = "用戶信息"verbose_name_plural = verbose_nameclass UserWx(models.Model):user = models.ForeignKey(User, on_delete=models.DO_NOTHING, null=True, blank=True)openid = models.CharField(max_length=50, verbose_name='openid')nickname = models.CharField(max_length=255, verbose_name='用戶昵稱')sex = models.IntegerField(choices=USER_SEX, verbose_name='性別')province = models.CharField(max_length=255, verbose_name='省份')city = models.CharField(max_length=255, verbose_name='城市')country = models.CharField(max_length=255, verbose_name='國家')head_img_url = models.CharField(max_length=500, verbose_name='用戶頭像url')privilege = models.CharField(max_length=1000, verbose_name='用戶特權信息')created = models.DateTimeField(verbose_name=u"創建時間", editable=False, auto_now_add=True)updated = models.DateTimeField(verbose_name=u"修改時間", editable=False, auto_now=True)deleted = models.SmallIntegerField(default=0)class Meta:verbose_name = '用戶微信信息'db_table = 'tb_user_wx'思路及步驟
1. 查出數據,得到需要序列化的數據,本例直接使用QueryDict對象進行序列化。 2. 得到被序列化model的每一個字段。 3. 根據每一個字段不同的類型作出不同的處理。 4. 返回結果字典。 5. 使用json模塊,將字典轉成json。代碼
# 獲取查詢結果集 user_wx = UserWx.objects.select_related('user').all() # 獲取查詢的結果集 result_dict = model_serializer(user_wx) result = json.dumps(result_dict) # 將結果集轉成json即可# 調用序列化方法,注:以下可選參數為拓展功能,可先忽略,對功能實現沒有多大影響 def model_serializer(cls, choices=True, contains=(), excepts=('updated', 'deleted')):"""model轉json:param choices: 是否自動轉換choices:param cls: 需要轉的model:param contains: 包含的字段 (若有此參數,則只返回次參數包含字段):param excepts: 排除字段,默認排除更新時間與deleted (若有此參數,則排除此參數內的字段):return:實現models轉dict支持了時間格式轉化, foreignKey, choice類型轉義暫不支持 ManyToMany ManyToOne 等"""try:# 查詢是否有外鍵的數據,為保證性能,只會序列化使用了select_related()的外鍵數據。foreign_dict = cls.query.select_relatedexcept Exception:foreign_dict = {}return _model_serializer(cls, choices, contains, excepts, foreign_dict)def _model_serializer(cls, choices, contains, excepts, foreign_dict={}):# 此方法主要用于判斷傳入的序列化對象是QueryDict或者是一個model。如果是QueryDict則返回list(dict),如果是model,則返回dictmodel_dict = []single = Falseif not isinstance(cls, Iterable):cls = (cls,)single = Truefor instance in cls: # 如果是QueryDict,則遍歷每一條數據model_dict.append(_model_to_dict(instance, choices, contains, excepts, foreign_dict))if single:model_dict = model_dict[0]return model_dictdef _model_to_dict(instance, choices, contains, excepts, foreign_dict):"""model 轉 dict:param foreign_dict::param contains::param excepts::param instance::return: dict"""field_dict = {}foreign_list = foreign_dict.keys() if isinstance(foreign_dict, dict) else () # 待序列化的外鍵列表fields = instance._meta.fields # 獲取當前期model的所有字段for field in fields:# 獲取字段名。Django有兩個字段屬性,name和attname。name是model里面寫的屬性名,attname則是 name_id(針對Foreign等)name = field.name # 排除不需要的參數(為拓展功能內容)if contains and (name not in contains):continueif name in excepts:continuetry:# 首先判斷是否為ForeignKey類型,ForeignKey需要用到name與attname,所以先單獨處理if isinstance(field, models.ForeignKey):if name in foreign_list: # 如果ForeignKey字段包含在select_related(),在需要對ForeignKey的model做序列化,否則直接返回ForeignKey的值value = getattr(instance, name)foreign_list_child = foreign_dict.get(name)# 如果有數據,則遞歸value = _model_serializer(value, choices=choices, contains=contains,excepts=excepts, foreign_dict=foreign_list_child)else:value = getattr(instance, field.attname)value = value if value else ''field_dict[name] = valuecontinueelse:value = getattr(instance, name)value = value if value else ''except Exception as e:field_dict[name] = ''continue# 上面處理了model.ForeignKey類型,下面處理其他類型。(類型不完全,可根據自己的需求添加)if isinstance(field, models.DateTimeField): # datetimefield_dict[name] = value.strftime('%Y-%m-%d %H:%M:%S') if value else ''elif isinstance(field, models.DateField): # datefield_dict[name] = value.strftime('%Y-%m-%d') if value else ''else: # 其他# 判斷是否有choices。(拓展功能,自動將有choices的值轉化為相應的描述)if choices and hasattr(field, 'choices') and field.choices:f = getattr(instance, 'get_{}_display'.format(name))field_dict[name] = f()else:field_dict[name] = valuereturn field_dict總結
以上第關鍵的代碼做了一些解釋說明,相信小伙伴們仔細思考應該能動。代碼的功能實現總體來說還是很簡單的,只需要得到別序列化對象的 字段,然后根據字段的類型作出相應的操作。但是上述代碼還有很多待優化的地方,下面給出一些建議(這個建議不保證能實現,只是一些方向)。 1.減少循環次數。2.由于遍歷的結果集大部分情況數據都是相同的,且相同的model的數據順序也是相同的,我們沒必要對每一天數據都做類型判 斷,可以在第一次循環后,緩存相關數據類型與操作。3.更多的拓展功能,比如只是OneToOne,ManyToMany等。最后
帖子有什么不當的地方,歡迎大佬指出。總結
以上是生活随笔為你收集整理的django 不包括字段 序列化器_手写一个Django序列化功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cfiledialog指定位置和大小_G
- 下一篇: 远程登录linux_linux 检查远程