代码之家  ›  专栏  ›  技术社区  ›  Vadorequest

django 1.8 makemigrations每次由于验证程序而生成重复的迁移

  •  0
  • Vadorequest  · 技术社区  · 7 年前

    我有一个模型,它有一个带有以下验证器的字段:

    validators=[
            FileValidator(
                allowed_extensions=ALLOWED_PHOTO_EXT,
                allowed_mimetypes=ALLOWED_PHOTO_MIME_TYPES,
                max_size=ALLOWED_PHOTO_MAX_SIZE,
            )
        ],
    

    这里是验证器本身

    @deconstructible
    class FileValidator(object):
        """
        Validator for files, checking the size, extension and mimetype.
        Initialization parameters:
            allowed_extensions: iterable with allowed file extensions
                ie. ('txt', 'doc')
            allowd_mimetypes: iterable with allowed mimetypes
                ie. ('image/png', )
            min_size: minimum number of bytes allowed
                ie. 100
            max_size: maximum number of bytes allowed
                ie. 24*1024*1024 for 24 MB
        Usage example::
            MyModel(models.Model):
                myfile = FileField(validators=FileValidator(max_size=24*1024*1024), ...)
    
        See https://gist.github.com/jrosebr1/2140738 (improved)
        """
    
        extension_message = _("Extension '%(extension)s' not allowed. Allowed extensions are: '%(allowed_extensions)s.'")
        mime_message = _("MIME type '%(mimetype)s' is not valid. Allowed types are: %(allowed_mimetypes)s.")
        min_size_message = _('The current file %(size)s, which is too small. The minumum file size is %(allowed_size)s.')
        max_size_message = _('The current file %(size)s, which is too large. The maximum file size is %(allowed_size)s.')
    
        def __init__(self, *args, **kwargs):
            self.allowed_extensions = kwargs.pop('allowed_extensions', None)
            self.allowed_mimetypes = kwargs.pop('allowed_mimetypes', None)
            self.min_size = kwargs.pop('min_size', 0)
            self.max_size = kwargs.pop('max_size', DEFAULT_FILE_MAX_SIZE)
    
        def __call__(self, value):
            """
            Check the extension, content type and file size.
            """
    
            # Check the extension
            ext = splitext(value.name)[1][1:].lower()
            if self.allowed_extensions and not ext in self.allowed_extensions:
                message = self.extension_message % {
                    'extension': ext,
                    'allowed_extensions': ', '.join(self.allowed_extensions)
                }
    
                raise ValidationError(message)
    
            # Check the content type
            # mimetype = mimetypes.guess_type(value.name)[0] # XXX Alternative guessing way, unsure
            mimetype = magic.from_buffer(value.read(1024), mime=True)
            if self.allowed_mimetypes and not mimetype in self.allowed_mimetypes:
                message = self.mime_message % {
                    'mimetype': mimetype,
                    'allowed_mimetypes': ', '.join(self.allowed_mimetypes)
                }
    
                raise ValidationError(message)
    
            # Check the file size
            filesize = len(value)
            if self.max_size and filesize > self.max_size:
                message = self.max_size_message % {
                    'size': filesizeformat(filesize),
                    'allowed_size': filesizeformat(self.max_size)
                }
    
                raise ValidationError(message)
    
            elif filesize < self.min_size:
                message = self.min_size_message % {
                    'size': filesizeformat(filesize),
                    'allowed_size': filesizeformat(self.min_size)
                }
    
                raise ValidationError(message)
    

    每次我跑步 ./manage.py makemigrations app1

    0003_auto_20180521_0325.py:
        - Alter field xxx on yyy
    

    它每次都会生成一个新的迁移,始终执行相同的操作(alter field)

    如果我对我的验证器发表评论,这个行为就会停止,并且makemigrations会显示一条“未检测到更改”消息。

    怎么了?我怎样才能避免这种行为?


    编辑:按照@Benjamin的回答,这里是完整的代码,包括修复。( __eq__ 功能)

    # -*- coding: utf-8 -*-
    
    import magic
    
    from os.path import splitext
    from django.core.exceptions import ValidationError
    from django.utils.deconstruct import deconstructible
    from django.utils.translation import gettext_lazy as _
    from django.template.defaultfilters import filesizeformat
    
    DEFAULT_FILE_MAX_SIZE = 10 * 1024 * 1024  # 10Mo
    
    # Photo, like profile picture
    # Only allow web-friendly extensions/mime types, since they'll be displayed on web pages
    # TODO Size is huge, should be optimised
    ALLOWED_PHOTO_EXT = [
        'jpg',
        'jpeg',
        'png',
    ]
    
    ALLOWED_PHOTO_MIME_TYPES = [
        'image/jpeg',
        'image/pjpeg',
        'image/png',
        'image/x-png',
    ]
    
    ALLOWED_PHOTO_MAX_SIZE = 10 * 1024 * 1024  # 10Mo
    
    # Any document
    # Allow a wide range of extensions and mime types
    # TODO Size is huge, should be optimised
    ALLOWED_DOCUMENT_EXT = [
        'jpg',
        'jpeg',
        'png',
        'tif',
        'bmp',
        'pdf',
        'doc',
        'dot',
        'docx',
        'dotx',
        'xls',
        'xlt',
        'xla',
        'xlsx',
        'xltx',
        'pptx',
        'potx',
        'ppsx',
    ]
    
    ALLOWED_DOCUMENT_MIME_TYPES = [
        'image/jpeg',
        'image/pjpeg',
        'image/png',
        'image/x-png',
        'image/tiff',
        'image/bmp',
        'application/pdf',
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'application/vnd.ms-powerpoint',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'application/zip',  # XXX PPTX can be detected as ZIP for some reasons
    ]
    
    ALLOWED_DOCUMENT_MAX_SIZE = 10 * 1024 * 1024  # 10Mo
    
    # Any document sent to SMoney, which have their own rules and limitations
    # Allow a wide range of extensions and mime types
    # TODO Size is huge, should be optimised
    ALLOWED_DOCUMENT_SMONEY_EXT = [
        'jpg',
        'jpeg',
        'png',
        'tif',
        'bmp',
        'pdf',
    ]
    
    ALLOWED_DOCUMENT_SMONEY_MIME_TYPES = [
        'image/jpeg',
        'image/pjpeg',
        'image/png',
        'image/x-png',
        'image/tiff',
        'image/bmp',
        'application/pdf',
    ]
    
    ALLOWED_DOCUMENT_SMONEY_MAX_SIZE = 3 * 1024 * 1024  # 3Mo
    
    
    @deconstructible
    class FileValidator(object):
        """
        Validator for files, checking the size, extension and mimetype.
        Initialization parameters:
            allowed_extensions: iterable with allowed file extensions
                ie. ('txt', 'doc')
            allowd_mimetypes: iterable with allowed mimetypes
                ie. ('image/png', )
            min_size: minimum number of bytes allowed
                ie. 100
            max_size: maximum number of bytes allowed
                ie. 24*1024*1024 for 24 MB
        Usage example::
            MyModel(models.Model):
                myfile = FileField(validators=FileValidator(max_size=24*1024*1024), ...)
    
        See https://gist.github.com/jrosebr1/2140738 (improved)
        """
    
        extension_message = _("Extension '%(extension)s' not allowed. Allowed extensions are: '%(allowed_extensions)s.'")
        mime_message = _("MIME type '%(mimetype)s' is not valid. Allowed types are: %(allowed_mimetypes)s.")
        min_size_message = _('The current file %(size)s, which is too small. The minumum file size is %(allowed_size)s.')
        max_size_message = _('The current file %(size)s, which is too large. The maximum file size is %(allowed_size)s.')
    
        def __init__(self, *args, **kwargs):
            self.allowed_extensions = kwargs.pop('allowed_extensions', None)
            self.allowed_mimetypes = kwargs.pop('allowed_mimetypes', None)
            self.min_size = kwargs.pop('min_size', 0)
            self.max_size = kwargs.pop('max_size', DEFAULT_FILE_MAX_SIZE)
    
        def __call__(self, value):
            """
            Check the extension, content type and file size.
            """
    
            # Check the extension
            ext = splitext(value.name)[1][1:].lower()
            if self.allowed_extensions and not ext in self.allowed_extensions:
                message = self.extension_message % {
                    'extension': ext,
                    'allowed_extensions': ', '.join(self.allowed_extensions)
                }
    
                raise ValidationError(message)
    
            # Check the content type
            # mimetype = mimetypes.guess_type(value.name)[0] # XXX Alternative guessing way, unsure
            mimetype = magic.from_buffer(value.read(1024), mime=True)
            if self.allowed_mimetypes and not mimetype in self.allowed_mimetypes:
                message = self.mime_message % {
                    'mimetype': mimetype,
                    'allowed_mimetypes': ', '.join(self.allowed_mimetypes)
                }
    
                raise ValidationError(message)
    
            # Check the file size
            filesize = len(value)
            if self.max_size and filesize > self.max_size:
                message = self.max_size_message % {
                    'size': filesizeformat(filesize),
                    'allowed_size': filesizeformat(self.max_size)
                }
    
                raise ValidationError(message)
    
            elif filesize < self.min_size:
                message = self.min_size_message % {
                    'size': filesizeformat(filesize),
                    'allowed_size': filesizeformat(self.min_size)
                }
    
                raise ValidationError(message)
    
        def __eq__(self, other):
            return (
                    isinstance(other, self.__class__) and
                    self.allowed_extensions == other.allowed_extensions and
                    self.allowed_mimetypes == other.allowed_mimetypes and
                    self.min_size == self.min_size and
                    self.max_size == self.max_size
            )
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   Vadorequest    7 年前

    我想这是因为 __eq__ 验证程序类中的方法。默认比较中的某些内容在两个以相同方式初始化的实例上看起来“不相等” __dict__ . 尝试添加显式:

    def __eq__(self, other):
        return (isinstance(other, self.__class__) and 
                self.allowed_extensions == other.allowed_extensions and
                self.allowed_mimetypes == other.allowed_mimetypes and
                self.min_size == self.min_size and
                self.max_size == self.max_size
        )
    
        2
  •  0
  •   Amine Messaoudi    7 年前
    /app_name/migrations
    

    删除所有.py文件,除了 _初始版本

    python manage.py makemigrations
    python manage.py migrate
    

    如果这不起作用,你可以试试我已经做过的事

    在视图中,我检查图像是否像这样有效

    def check_image(request):
    
        if len(request.FILES) != 0:
    
            image = request.FILES['image'] # <input type="file" name="image" ....>
            ext = os.path.splitext(image.name)[1]
    
            if ext in ['.jpg', '.jpeg', '.png', '.gif']:
                # file is image
                x=0
            else:
                # file is not image
                y=0
    
        return HttpResponseRedirect('/')