首頁 > 後端開發 > Python教學 > 看看 Python Django開發 異常及解決辦法

看看 Python Django開發 異常及解決辦法

coldplay.xixi
發布: 2021-01-07 10:25:52
轉載
2559 人瀏覽過

看看 Python Django開發 異常及解決辦法

相關免費學習推薦:python影片教學

1.Django xadmin資料遷移報錯ImportError: cannot import name 'QUERY_TERMS'

在進行Django xadmin資料遷移時報錯:

from django.db.models.sql.query import LOOKUP_SEP, QUERY_TERMS
ImportError: cannot import name 'QUERY_TERMS' from 'django.db.models.sql.query' (C:\Users\LENOVO\.virtualenvs\Django_Vue_Fresh_Ecommerce-NKba4OvD\lib\site-packages\django\db\models\sql\query.py)
登入後複製

由於xadmin的更新跟不上Django的更新,因此導致了xadmin的許多程式碼出錯,需要進行修改,這裡將xadmin\plugins\filters.py中from django.db.models.sql.query import LOOKUP_SEP, QUERY_TERMS#修改為from django.db.models.sql.query import LOOKUP_SEP, Query,也需要將47行的if len(parts) > 1 and parts[-1] in Query:修改為if len(parts) > 1 and parts[-1] in QUERY_TERMS:

2.Django xadmin報錯TypeError: render() got an unexpected keyword argument 'renderer'

在Django登入進入xadmin後台時,在新增小工具時,會報錯,如下:

return widget.render(TypeError: render() got an unexpected keyword argument 'renderer'
登入後複製

解決方法有兩種:

  • 修改Django原始碼
    找到lib\site-packages\django\forms\boundfield.py,找到第96行,註解掉即可,如下:
return widget.render(
    name=self.html_initial_name if only_initial else self.html_name,
    value=self.value(),
    attrs=attrs,
    # renderer=self.form.renderer,)
登入後複製

此時再點擊Add Budgets就不會再報錯了。

  • 修改xadmin程式碼
    在xadmin/views/dashboard.py中修改render()函數,第36行改為def render(self, name , value, attrs=None, renderer=None):,即增加renderer參數為None。

兩種方法皆可,但個人建議採用第二種方法,因為xadmin是外部引入到extra_apps作為外部的app,本身就可能經過了一定修改,在此基礎上再修改也影響不大,而django是虛擬環境所帶的依賴庫,相當於系統文件,因此不要輕易修改。

3.Django xadmin報錯RuntimeError: isn't in an application in INSTALLED_APPS.

##在進行資料庫對應時,報錯如下:

raise RuntimeError(RuntimeError: Model class django.contrib.admin.models.LogEntry doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
登入後複製
#解決方法是在settings.py中的INSTALLED_APPS中增加

django.contrib.admin,如下:

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.users.apps.UsersConfig',
    'goods',
    'trade',
    'user_operation',
    'DjangoUeditor',
    'xadmin',
    'crispy_forms',
    'django.contrib.admin']
登入後複製

4.Django配置Restful framework報錯__str__ returned non-string ( type NoneType)

在Django專案中配置Restful framework時,報錯__str__ returned non-string (type NoneType),如下:


看看 Python Django開發 異常及解決辦法 這可能是自訂用戶當模型取代Django自帶的使用者模型時,允許name(或相似的)欄位允許為空,例如
name = models.CharField(max_length=30, null=True, blank=True, verbose_name='姓名') 所以會回傳non-string報錯,完整模型如下:

class UserProfile(AbstractUser):
    '''用户'''
    name = models.CharField(max_length=30, null=True, blank=True, verbose_name='姓名')
    birthday = models.DateField(null=True, blank=True, verbose_name='出生日期')
    gender = models.CharField(max_length=6, choices=(('male', u'男'), ('female', u'女')), default='female',
                              verbose_name='性别')
    mobile = models.CharField(max_length=11, verbose_name='电话')
    email = models.CharField(max_length=50, null=True, blank=True, verbose_name='邮箱')

    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '用户'
        verbose_name_plural = '用户'

    def __str__(self):
        return self.name
登入後複製
解決方法有2種:

    退出admin或xadmin後台登入
  • 登出後台管理登錄,操作如下:

    django xadmin logout
  • 修改使用者模型
  • __str__()方法 因為自訂使用者如UserProfile繼承自AbstractUser,而AbstractUser模型有username屬性,不允許為空,所以可以設定為回傳
    self.username,即如下:
def __str__(self):
    return self.username
登入後複製
此時不登出後台管理也可以正常存取。

5.DRF封包錯誤AssertionError: basename argument not specified

在Restful framework中使用篩選器報錯誤:

assert queryset is not None, '`basename` argument not specified, and could ' \
AssertionError: `basename` argument not specified, and could not automatically determine the name from the viewset, as it does not have a `.queryset` attribute.
登入後複製
報錯提示很明顯,

assert queryset不是None,未指定「basename」參數,顯然需要在使用router定義路由時指定basename參數,如下:

router = DefaultRouter()# 配置goods的路由router.register(r'goods', GoodsListViewSet, basename='goods')
登入後複製
即在urls.py中使用router設定路由時加入basename參數即可。

6.UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list

paginator = self.django_paginator_class(queryset, page_size)#dameDamejwork 在實作Dameango 在實作Dameango Rful視圖時對某一類資料進行分頁並在前端請求存取資料時顯示警告資訊如下:

XXX\lib\site-packages\rest_framework\pagination.py:200: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class> QuerySet.
  paginator = self.django_paginator_class(queryset, page_size)</class>
登入後複製

提示的是無序物件清單警告,意思是對資料結果進行排序,在views.py中取資料時加入排序即可,預設可以依照id排序,示意如下:
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    '''商品列表页,并实现分页、搜索、过滤、排序'''

    queryset = Goods.objects.filter(is_delete=False).order_by('id')  # 添加根据id排序即可
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_class = GoodsFilter
    search_fields = ['name', 'goods_brief', 'goods_desc']
    ordering_fields = ['sold_num', 'shop_price']
登入後複製

此時再運行,不再顯示警告訊息。

7.Django Restful framework中使用JWT實作自訂驗證{「non_field_errors」:[「無法使用提供的認證資訊登入。」]}

#先聲明小編使用的Django版本為3.0,後面有用。

在DRF中使用驗證時常會使用JSON Web Token進行驗證,settings.py配置如下:

# DRF配置REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]}# 自定义用户认证配置AUTHENTICATION_BACKENDS = [
    'users.views.CustomBackend',]
登入後複製

apps/users/views.py如下:
from django.db.models import Qfrom django.contrib.auth.backends import ModelBackendfrom django.contrib.auth import get_user_model


User = get_user_model()# Create your views here.class CustomBackend(ModelBackend):
    '''自定义用户验证'''

    def authenticate(self, username=None, password=None, **kwargs):
        try:
            print(123)
            user = User.objects.get(Q(username=username)|Q(mobile=username))
            if user.check_password(password) and user.is_delete != True:
                print(456)
                return user        except Exception as e:
            return None
登入後複製

urls.py配置如下:

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
       # JWT认证路由
       url(r'^login/', obtain_jwt_token),]
登入後複製

但是在模拟请求访问时却未收到token,只提示错误信息{"non_field_errors":["无法使用提供的认证信息登录。"]},这让我很苦恼,明明所有配置都没问题啊,百思不得姐,到底哪里出了问题了呢?一直不停的排错、Debug,却还是一样的错误,这让我很郁闷。最后不得不去求助于JWT官方文档,看到环境要求仿佛有点儿感觉了:

Requirements
Python (2.7, 3.3, 3.4, 3.5)
Django (1.8, 1.9, 1.10)
Django REST Framework (3.0, 3.1, 3.2, 3.3, 3.4, 3.5)

这里要求的最高Django版本为1.9,而我自己的Django版本为3.0,凭直觉立马想到会不会是版本不兼容的问题,导致了某些地方不一致。jwt部分就不说了,本身版本没怎么更新,可能问题出在了Django和DRF上面,而最有可能出问题的就是自定义验证类,CustomBackend继承自ModelBackend,于是我到django.contrib.auth.backends源码中查看,其定义如下:

class ModelBackend(BaseBackend):
    """
    Authenticates against settings.AUTH_USER_MODEL.
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        if username is None or password is None:
            return
        try:
            user = UserModel._default_manager.get_by_natural_key(username)
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user	...
登入後複製

为了验证是否是版本的问题,我在系统环境中安装了JWT指定的Django版本1.9用于进行对比,再查看django.contrib.auth.backends.py:

class ModelBackend(object):
    """
    Authenticates against settings.AUTH_USER_MODEL.
    """

    def authenticate(self, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        try:
            user = UserModel._default_manager.get_by_natural_key(username)
            if user.check_password(password):
                return user        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a non-existing user (#20760).
            UserModel().set_password(password)
登入後複製

到现在,你们是否发现了什么(^_^)?

哈哈,你猜的没错,是新版中的authenticate()方法发生了改变,增加了request参数,而自定义验证类时就是继承ModelBackend类并重写authenticate()方法,而我使用的参数采用的是老版本中的参数,与本应继承的新版本中的方法参数不一致,所以就不是重写是重载了,所以在请求时验证调用的方法并不是自定义的authenticate(),而是ModelBackend类中的authenticate()方法明白怎么回事了就赶紧改了试试,再次测试{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTk1ODk2MTc3LCJlbWFpbCI6IjEyM0AxMjMuY29tIn0.pblxNy4s4XBrqmnsfI9-dmx3Q8rErqq1WbN4rfBSZfI"},一片光明,真是版本不兼容害苦了我,以后得注意了。

8.Django使用DRF实现注册功能报错Got AttributeError when attempting to get a value

在使用DRF实现注册功能时,前端的用户名(手机号)、验证码、邮箱传到后端处理时,由于验证码不属于用户的一个字段,但是为了验证又必须设置该字段,如果不注意,就容易报错,一般如下:

raise type(exc)(msg)AttributeError: Got AttributeError when attempting to get a value for field `code` on serializer `UserRegSerializer`.The serializer field might be named incorrectly and not match any attribute or key on the `UserProfile` instance.Original exception text was: 'UserProfile' object has no attribute 'code'.
登入後複製

报错提示很明显,UserProfile没有code属性。具体来说,这是因为Meta中指定了fields = ['username', 'code', 'mobile', 'password'],包含code字段,而在验证时为了判断验证码的正误而临时加入code字段,但是在validate(attrs)又将其删去,导致在序列化时找不到code字段,因此出错,这是需要将字段的write_only设置True,以确保在更新或创建实例时可以使用该字段,但是在序列化表示形式时不包括该字段,即设置为如下即可:

code = serializers.CharField(max_length=4, min_length=4, write_only=True, label='验证码',
    help_text='验证码',
    error_messages={
        'required': '请输入验证码',
        'blank': '请输入验证码',
        'max_length': '请输入4位验证码',
        'min_length': '请输入4位验证码'
    })
登入後複製

9.DRF访问文档路由报错AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’

DRF提供了文档功能,无需再专门写文档即可同步使用文档,但是在访问http://127.0.0.1:8000/docs/的时候可能报错:

    link = view.schema.get_link(path, method, base_url=self.url)AttributeError: 'AutoSchema' object has no attribute 'get_link'
登入後複製

此时需要在settings.py中进行配置:

# DRF配置REST_FRAMEWORK = {
    ...
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    ...}
登入後複製

重新加载之后再次访问就会访问到文档页面,如下:
DRF 文档

10.DRF动态设置权限

在DRF中经常会用到权限,一般情况下是在视图ViewSet类下设置属性permission_classes = [IsAuthenticated, IsOwnerOrReadOnly],但是这对于请求的所有方法(如create、retrieve、list)均有效,不能对不同的方法进行不同的限制,因此可以进行动态设置权限,即重写get_permissions()方法,针对不同地方法返回不同的权限,如下:

    def get_permissions(self):
        '''动态设置权限'''
        if self.action == 'retrieve':
            return [IsAuthenticated]
        elif self.action == 'create':
            return []
        return []
登入後複製

但是会报错如下:

if not permission.has_permission(request, self):TypeError: has_permission() missing 1 required positional argument: 'view'
登入後複製

这是因为返回的可能是权限类,即return [IsAuthenticated],这里只是返回了一个权限类,并没有实例化,即没有初始化,导致APIView在初始化时没有传入正确的权限,因此报错,修改为return [IsAuthenticated()]、返回实例化后的对象即可。

以上是看看 Python Django開發 異常及解決辦法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板