标签归档:Django

Django URL重定向

问题:Django URL重定向

如何将与其他URL不匹配的流量重定向回首页?

urls.py:

urlpatterns = patterns('',
    url(r'^$', 'macmonster.views.home'),
    #url(r'^macmon_home$', 'macmonster.views.home'),
    url(r'^macmon_output/$', 'macmonster.views.output'),
    url(r'^macmon_about/$', 'macmonster.views.about'),
    url(r'^.*$',  'macmonster.views.home'),
)

就目前而言,最后一个条目将所有“其他”流量发送到主页,但是我想通过HTTP 301302进行重定向。

How can I redirect traffic that doesn’t match any of my other URLs back to the home page?

urls.py:

urlpatterns = patterns('',
    url(r'^$', 'macmonster.views.home'),
    #url(r'^macmon_home$', 'macmonster.views.home'),
    url(r'^macmon_output/$', 'macmonster.views.output'),
    url(r'^macmon_about/$', 'macmonster.views.about'),
    url(r'^.*$',  'macmonster.views.home'),
)

As it stands, the last entry sends all “other” traffic to the home page but I want to redirect via either an HTTP 301 or 302.


回答 0

您可以尝试称为 RedirectView

from django.views.generic.base import RedirectView

urlpatterns = patterns('',
    url(r'^$', 'macmonster.views.home'),
    #url(r'^macmon_home$', 'macmonster.views.home'),
    url(r'^macmon_output/$', 'macmonster.views.output'),
    url(r'^macmon_about/$', 'macmonster.views.about'),
    url(r'^.*$', RedirectView.as_view(url='<url_to_home_view>', permanent=False), name='index')
)

请注意url<url_to_home_view>您实际需要如何指定url。

permanent=False将返回HTTP 302,而permanent=True将返回HTTP 301。

或者,您可以使用 django.shortcuts.redirect

Django 2+版本的更新

在Django 2+中,url()已弃用并替换为re_path()。用法url()与正则表达式完全相同。对于不需要正则表达式的替换,请使用path()

from django.urls import re_path

re_path(r'^.*$', RedirectView.as_view(url='<url_to_home_view>', permanent=False), name='index')

You can try the Class Based View called RedirectView

from django.views.generic.base import RedirectView

urlpatterns = patterns('',
    url(r'^$', 'macmonster.views.home'),
    #url(r'^macmon_home$', 'macmonster.views.home'),
    url(r'^macmon_output/$', 'macmonster.views.output'),
    url(r'^macmon_about/$', 'macmonster.views.about'),
    url(r'^.*$', RedirectView.as_view(url='<url_to_home_view>', permanent=False), name='index')
)

Notice how as url in the <url_to_home_view> you need to actually specify the url.

permanent=False will return HTTP 302, while permanent=True will return HTTP 301.

Alternatively you can use django.shortcuts.redirect

Update for Django 2+ versions

With Django 2+, url() is deprecated and replaced by re_path(). Usage is exactly the same as url() with regular expressions. For replacements without the need of regular expression, use path().

from django.urls import re_path

re_path(r'^.*$', RedirectView.as_view(url='<url_to_home_view>', permanent=False), name='index')

回答 1

在Django 1.8中,这就是我的工作方式。

from django.views.generic.base import RedirectView

url(r'^$', views.comingSoon, name='homepage'),
# whatever urls you might have in here
# make sure the 'catch-all' url is placed last
url(r'^.*$', RedirectView.as_view(pattern_name='homepage', permanent=False))

除了使用之外url,您还可以使用pattern_name,它有点不干,可以确保您更改了网址,也不必更改重定向。

In Django 1.8, this is how I did mine.

from django.views.generic.base import RedirectView

url(r'^$', views.comingSoon, name='homepage'),
# whatever urls you might have in here
# make sure the 'catch-all' url is placed last
url(r'^.*$', RedirectView.as_view(pattern_name='homepage', permanent=False))

Instead of using url, you can use the pattern_name, which is a bit un-DRY, and will ensure you change your url, you don’t have to change the redirect too.


回答 2

如果像我一样坚持使用django 1.2,并且RedirectView不存在,则添加路由映射的另一种以路由为中心的方法是使用:

(r'^match_rules/$', 'django.views.generic.simple.redirect_to', {'url': '/new_url'}),  

您还可以重新路由比赛中的所有内容。这在更改应用程序的文件夹但想要保留书签时非常有用:

(r'^match_folder/(?P<path>.*)', 'django.views.generic.simple.redirect_to', {'url': '/new_folder/%(path)s'}),  

如果您仅尝试修改url路由并且无权访问.htaccess等,则此方法比django.shortcuts.redirect更可取。 .htaccess)。

If you are stuck on django 1.2 like I am and RedirectView doesn’t exist, another route-centric way to add the redirect mapping is using:

(r'^match_rules/$', 'django.views.generic.simple.redirect_to', {'url': '/new_url'}),  

You can also re-route everything on a match. This is useful when changing the folder of an app but wanting to preserve bookmarks:

(r'^match_folder/(?P<path>.*)', 'django.views.generic.simple.redirect_to', {'url': '/new_folder/%(path)s'}),  

This is preferable to django.shortcuts.redirect if you are only trying to modify your url routing and do not have access to .htaccess, etc (I’m on Appengine and app.yaml doesn’t allow url redirection at that level like an .htaccess).


回答 3

做到这一点的另一种方法是使用HttpResponsePermanentRedirect,如下所示:

在view.py

def url_redirect(request):
    return HttpResponsePermanentRedirect("/new_url/")

在url.py中

url(r'^old_url/$', "website.views.url_redirect", name="url-redirect"),

Another way of doing it is using HttpResponsePermanentRedirect like so:

In view.py

def url_redirect(request):
    return HttpResponsePermanentRedirect("/new_url/")

In the url.py

url(r'^old_url/$', "website.views.url_redirect", name="url-redirect"),

回答 4

其他方法也可以,但是您也可以使用旧方法django.shortcut.redirect

下面的代码是从此答案中获取的

在Django 2.x中:

from django.shortcuts import redirect
from django.urls import path, include

urlpatterns = [
    # this example uses named URL 'hola-home' from app named hola
    # for more redirect's usage options: https://docs.djangoproject.com/en/2.1/topics/http/shortcuts/
    path('', lambda request: redirect('hola/', permanent=True)),
    path('hola/', include('hola.urls')),
]

The other methods work fine, but you can also use the good old django.shortcut.redirect.

The code below was taken from this answer.

In Django 2.x:

from django.shortcuts import redirect
from django.urls import path, include

urlpatterns = [
    # this example uses named URL 'hola-home' from app named hola
    # for more redirect's usage options: https://docs.djangoproject.com/en/2.1/topics/http/shortcuts/
    path('', lambda request: redirect('hola/', permanent=True)),
    path('hola/', include('hola.urls')),
]

django模板中的“ none”是什么意思?

问题:django模板中的“ none”是什么意思?

我想看看Django模板中是否没有字段/变量。正确的语法是什么?

这是我目前拥有的:

{% if profile.user.first_name is null %}
  <p> -- </p>
{% elif %}
  {{ profile.user.first_name }} {{ profile.user.last_name }}
{% endif%}

在上面的示例中,我将用什么来替换“空”?

I want to see if a field/variable is none within a Django template. What is the correct syntax for that?

This is what I currently have:

{% if profile.user.first_name is null %}
  <p> -- </p>
{% elif %}
  {{ profile.user.first_name }} {{ profile.user.last_name }}
{% endif%}

In the example above, what would I use to replace “null”?


回答 0

None, False and True所有这些都可在模板标记和过滤器中找到。None, False,空字符串('', "", """""")和空列表/元组False都由进行求值if,因此您可以轻松地执行

{% if profile.user.first_name == None %}
{% if not profile.user.first_name %}

提示:@fabiocerqueira是正确的,将逻辑留给模型,将模板限制为唯一的表示层,并计算模型中的内容。一个例子:

# someapp/models.py
class UserProfile(models.Model):
    user = models.OneToOneField('auth.User')
    # other fields

    def get_full_name(self):
        if not self.user.first_name:
            return
        return ' '.join([self.user.first_name, self.user.last_name])

# template
{{ user.get_profile.get_full_name }}

希望这可以帮助 :)

None, False and True all are available within template tags and filters. None, False, the empty string ('', "", """""") and empty lists/tuples all evaluate to False when evaluated by if, so you can easily do

{% if profile.user.first_name == None %}
{% if not profile.user.first_name %}

A hint: @fabiocerqueira is right, leave logic to models, limit templates to be the only presentation layer and calculate stuff like that in you model. An example:

# someapp/models.py
class UserProfile(models.Model):
    user = models.OneToOneField('auth.User')
    # other fields

    def get_full_name(self):
        if not self.user.first_name:
            return
        return ' '.join([self.user.first_name, self.user.last_name])

# template
{{ user.get_profile.get_full_name }}

Hope this helps :)


回答 1

您还可以使用其他内置模板 default_if_none

{{ profile.user.first_name|default_if_none:"--" }}

You can also use another built-in template default_if_none

{{ profile.user.first_name|default_if_none:"--" }}

回答 2

is运算符:Django 1.10中的新增功能

{% if somevar is None %}
  This appears if somevar is None, or if somevar is not found in the context.
{% endif %}

isoperator : New in Django 1.10

{% if somevar is None %}
  This appears if somevar is None, or if somevar is not found in the context.
{% endif %}

回答 3

看看yesno助手

例如:

{{ myValue|yesno:"itwasTrue,itWasFalse,itWasNone" }}

Look at the yesno helper

Eg:

{{ myValue|yesno:"itwasTrue,itWasFalse,itWasNone" }}

回答 4

{% if profile.user.first_name %}起作用(假设您也不想接受'')。

if在Python一般对待NoneFalse''[]{},…所有为假。

{% if profile.user.first_name %} works (assuming you also don’t want to accept '').

if in Python in general treats None, False, '', [], {}, … all as false.


回答 5

您还可以使用内置的模板过滤器default

如果value的值为False(例如None,一个空字符串,0,False);默认显示为“-”。

{{ profile.user.first_name|default:"--" }}

文档:https : //docs.djangoproject.com/en/dev/ref/templates/builtins/#default

You can also use the built-in template filter default:

If value evaluates to False (e.g. None, an empty string, 0, False); the default “–” is displayed.

{{ profile.user.first_name|default:"--" }}

Documentation: https://docs.djangoproject.com/en/dev/ref/templates/builtins/#default


回答 6

您可以尝试以下方法:

{% if not profile.user.first_name.value %}
  <p> -- </p>
{% else %}
  {{ profile.user.first_name }} {{ profile.user.last_name }}
{% endif %}

这样,您实际上是在检查表单字段first_name是否具有与其关联的任何值。见{{ field.value }}循环遍历Django文档形式的领域

我正在使用Django 3.0。

You could try this:

{% if not profile.user.first_name.value %}
  <p> -- </p>
{% else %}
  {{ profile.user.first_name }} {{ profile.user.last_name }}
{% endif %}

This way, you’re essentially checking to see if the form field first_name has any value associated with it. See {{ field.value }} in Looping over the form’s fields in Django Documentation.

I’m using Django 3.0.


Django模型中字段的默认值

问题:Django模型中字段的默认值

假设我有一个模型:

class SomeModel(models.Model):
    id = models.AutoField(primary_key=True)
    a = models.CharField(max_length=10)
    b = models.CharField(max_length=7)

目前,我正在使用默认的admin创建/编辑此类型的对象。如何b从管理员中删除该字段,以使每个对象都无法创建一个值,而是会收到默认值0000000

Suppose I have a model:

class SomeModel(models.Model):
    id = models.AutoField(primary_key=True)
    a = models.CharField(max_length=10)
    b = models.CharField(max_length=7)

Currently I am using the default admin to create/edit objects of this type. How do I remove the field b from the admin so that each object cannot be created with a value, and rather will receive a default value of 0000000?


回答 0

设置editableFalsedefault为默认值。

http://docs.djangoproject.com/en/stable/ref/models/fields/#editable

b = models.CharField(max_length=7, default='0000000', editable=False)

另外,您的id字段是不必要的。Django将自动添加它。

Set editable to False and default to your default value.

http://docs.djangoproject.com/en/stable/ref/models/fields/#editable

b = models.CharField(max_length=7, default='0000000', editable=False)

Also, your id field is unnecessary. Django will add it automatically.


回答 1

您可以这样设置默认值:

b = models.CharField(max_length=7,default="foobar")

然后您可以使用模型的Admin类隐藏字段,如下所示:

class SomeModelAdmin(admin.ModelAdmin):
    exclude = ("b")

You can set the default like this:

b = models.CharField(max_length=7,default="foobar")

and then you can hide the field with your model’s Admin class like this:

class SomeModelAdmin(admin.ModelAdmin):
    exclude = ("b")

回答 2

您还可以在默认字段中使用callable,例如:

b = models.CharField(max_length=7, default=foo)

然后定义可调用项:

def foo():
    return 'bar'

You can also use a callable in the default field, such as:

b = models.CharField(max_length=7, default=foo)

And then define the callable:

def foo():
    return 'bar'

如何编写setup.py以包含Git存储库作为依赖项

问题:如何编写setup.py以包含Git存储库作为依赖项

我正在尝试setup.py为我的包裹写东西。我的软件包需要指定对另一个Git存储库的依赖。

这是我到目前为止所拥有的:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

当我跑步时:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

我懂了

找不到满足SomePrivateLib> = 0.1.0要求的版本(来自分析)(来自版本:)找不到SomePrivateLib> = 0.1.0(来自分析)的匹配分布

我究竟做错了什么?

I am trying to write setup.py for my package. My package needs to specify a dependency on another git repo.

This is what I have so far:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

When I run:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

I get

Could not find a version that satisfies the requirement SomePrivateLib>=0.1.0 (from analyse) (from versions: ) No matching distribution found for SomePrivateLib>=0.1.0 (from analyse)

What am I doing wrong ?


回答 0

您可以在此处找到正确的方法。

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

关键不是提供指向Git存储库的链接,而是提供指向tarball的链接。如果您/tarball/master像上面那样添加,GitHub会为您创建master分支的压缩包。

You can find the right way to do it here.

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

The key is not to give a link to a git repository, but a link to a tarball. Github creates a tarball of the master branch for you if you append /tarball/master as shown above.


回答 1

在通过上面的评论中@muon链接的pip问题3939PEP-508规范后,我发现成功地通过setup.py使用以下规范模式来安装我的私有repo依赖项install_requires(不再dependency_links):

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

@v1.1指示在GitHub上创建的发布标志,并可以用一个分支,提交,或不同类型的标签来代替。

After digging through the pip issue 3939 linked by @muon in the comments above and then the PEP-508 specification, I found success getting my private repo dependency to install via setup.py using this specification pattern in install_requires (no more dependency_links):

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

The @v1.1 indicates the release tag created on github and could be replaced with a branch, commit, or different type of tag.


回答 2

以下答案不适用于Pip 19+


不幸的是,其他答案不适用于私有存储库,这是最常见的用例之一。我最终的确使用了如下setup.py文件:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

较新版本的pip消除了使用“ dependency_links”的需要,从而使操作更加轻松-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

The following answer is deprecated for Pip 19+


Unfortunately the other answer does not work with private repositories, which is one of the most common use cases for this. I eventually did get it working with a setup.py file that looks like this:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

Newer versions of pip make this even easier by removing the need to use “dependency_links”-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

回答 3

一个更一般的答案:要从requirements.txt文件中获取信息,请执行以下操作:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []

# Do not add to required lines pointing to Git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            required.append(package_name)
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
)

A more general answer, to get the information from the requeriments.txt file I do:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []
# do not add to required lines pointing to git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            required.append(package_name)
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
) 

回答 4

实际上,如果您希望递归安装软件包(YourCurrentPackage包含SomePrivateLib),例如,当您希望将YourCurrentPackage包含到另一个软件包中(例如OuterPackage→YourCurrentPackage→SomePrivateLib)时,您将同时需要:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

并确保您使用版本号创建了一个标签。

同样,如果您的Git项目是私有的,并且您想将其安装在容器中(例如DockerGitLab运行器),那么您将需要对存储库的授权访问。请考虑将Git + HTTPS与访问令牌一起使用(例如在GitLab上:https : //docs.gitlab.com/ee/user/profile/personal_access_tokens.html ):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

Actually if you like to make your packages installable recursivelly (YourCurrentPackage includes your SomePrivateLib), e.g. when you want to include YourCurrentPackage into another one (like OuterPackage -> YourCurrentPackage -> SomePrivateLib) you’ll need both:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

And make sure you have a tag created with your version number.

Also if your git project is private and you like to install it inside the container e.g. Docker or GitLab runner, you will need authorized access to your repo. Please consider to use git+https with access tokens (like on GitLab: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

回答 5

我在GitLab中成功使用了这三个选项。我正在使用GitLab版本11。

选项1-未指定令牌。Shell将提示输入用户名/密码。

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项2-指定了用户访问令牌。通过转到GitLab→帐户右上方→设置→访问令牌生成的令牌。创建具有read_repository权限的令牌。

例:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项3-指定存储库级别的令牌。通过转到存储库→设置→存储库→部署令牌生成的令牌。在这里,创建一个具有read_repository权限的令牌。

例:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

在这三者中,我都可以简单地做:“ SomePrivateLib @ git + https://gitlab.server.com/abc/SomePrivateLib.git”,最后没有#egg标记。

I was successful with these 3 options in gitlab. I am using version 11 of gitlab.

option 1 – no token specified. shell will prompt for username/password.

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

option 2 – user access token specified. token generated by going to gitlab > account top right > settings > access tokens. create token with read_repository rights.

example:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

option 3 – repository-level token specified. token generated by going to the repository > settings > repository > deploy tokens. from here create a token with read_repository rights.

example:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

In all 3, I was able to do simply: “SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git” without the #egg marking at the end.


Django-导入django.conf.settings和导入设置之间的区别

问题:Django-导入django.conf.settings和导入设置之间的区别

Django应用程序中以下import语句之间的基本区别是什么?

import settings

from django.conf import settings

What is the basic difference between the following import statements in a Django app?

import settings

and

from django.conf import settings

回答 0

import settings

将导入Django项目的settings(.py)模块(当然,如果您是从应用程序的“ root”包中编写此代码的话)

from django.conf import settings

将从django.conf包(Django提供的文件)中导入设置对象这很重要,因为

[..]请注意,您的代码不应从global_settings或您自己的设置文件中导入。django.conf.settings抽象了默认设置和特定于站点的设置的概念;它提供了一个界面。它还将使用设置的代码与设置的位置解耦。

更新:如果要定义一些自己的设置,请参阅文档的此部分

import settings

Will import settings(.py) module of your Django project (if you are writing this code from the “root” package of your application, of course)

from django.conf import settings

Will import settings object from django.conf package (Django’s provided files). This is important, because

[..] note that your code should not import from either global_settings or your own settings file. django.conf.settings abstracts the concepts of default settings and site-specific settings; it presents a single interface. It also decouples the code that uses settings from the location of your settings.

UPDATE: if you want to define some own settings, see this part of the documentation


如何访问表单的clean()方法中的请求对象或任何其他变量?

问题:如何访问表单的clean()方法中的请求对象或任何其他变量?

我试图在request.user中获取表单的clean方法,但是如何访问请求对象?我可以修改clean方法以允许输入变量吗?

I am trying to request.user for a form’s clean method, but how can I access the request object? Can I modify the clean method to allow variables input?


回答 0

Ber的答案-将其存储在threadlocals中-是一个非常糟糕的主意。绝对没有理由这样做。

更好的方法是重写表单的__init__方法以使用额外的关键字参数request。这会将请求存储在形式中,在需要的地方,您可以从此处使用干净的方法访问该请求。

class MyForm(forms.Form):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyForm, self).__init__(*args, **kwargs)


    def clean(self):
        ... access the request object via self.request ...

并且在您看来:

myform = MyForm(request.POST, request=request)

The answer by Ber – storing it in threadlocals – is a very bad idea. There’s absolutely no reason to do it this way.

A much better way is to override the form’s __init__ method to take an extra keyword argument, request. This stores the request in the form, where it’s required, and from where you can access it in your clean method.

class MyForm(forms.Form):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyForm, self).__init__(*args, **kwargs)


    def clean(self):
        ... access the request object via self.request ...

and in your view:

myform = MyForm(request.POST, request=request)

回答 1

更新时间2011年10月25日:我现在将它与动态创建的类(而不是方法)一起使用,因为Django 1.3否则会显示一些怪异之处。

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
        class ModelFormWithRequest(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return ModelFormWithRequest

然后重写MyCustomForm.__init__如下:

class MyCustomForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyCustomForm, self).__init__(*args, **kwargs)

然后,您可以使用ModelFormwith的任何方法访问请求对象self.request

UPDATED 10/25/2011: I’m now using this with a dynamically created class instead of method, as Django 1.3 displays some weirdness otherwise.

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
        class ModelFormWithRequest(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return ModelFormWithRequest

Then override MyCustomForm.__init__ as follows:

class MyCustomForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyCustomForm, self).__init__(*args, **kwargs)

You can then access the request object from any method of ModelForm with self.request.


回答 2

值得的是,如果您使用的是基于类的视图,而不是基于函数的视图,请get_form_kwargs在编辑视图中覆盖。自定义CreateView的示例代码:

from braces.views import LoginRequiredMixin

class MyModelCreateView(LoginRequiredMixin, CreateView):
    template_name = 'example/create.html'
    model = MyModel
    form_class = MyModelForm
    success_message = "%(my_object)s added to your site."

    def get_form_kwargs(self):
        kw = super(MyModelCreateView, self).get_form_kwargs()
        kw['request'] = self.request # the trick!
        return kw

    def form_valid(self):
        # do something

上面的视图代码将request作为表单__init__构造函数的关键字参数之一提供。因此,在您ModelForm执行以下操作:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        # important to "pop" added kwarg before call to parent's constructor
        self.request = kwargs.pop('request')
        super(MyModelForm, self).__init__(*args, **kwargs)

For what it’s worth, if you’re using Class Based Views, instead of function based views, override get_form_kwargs in your editing view. Example code for a custom CreateView:

from braces.views import LoginRequiredMixin

class MyModelCreateView(LoginRequiredMixin, CreateView):
    template_name = 'example/create.html'
    model = MyModel
    form_class = MyModelForm
    success_message = "%(my_object)s added to your site."

    def get_form_kwargs(self):
        kw = super(MyModelCreateView, self).get_form_kwargs()
        kw['request'] = self.request # the trick!
        return kw

    def form_valid(self):
        # do something

The above view code will make request available as one of the keyword arguments to the form’s __init__ constructor function. Therefore in your ModelForm do:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        # important to "pop" added kwarg before call to parent's constructor
        self.request = kwargs.pop('request')
        super(MyModelForm, self).__init__(*args, **kwargs)

回答 3

通常的方法是使用中间件将请求对象存储在线程本地引用中。然后,您可以从应用程序中的任何位置(包括Form.clean()方法)访问此文件。

更改Form.clean()方法的签名意味着您拥有自己的Django修改版,而这可能不是您想要的。

感谢中间件的数量看起来像这样:

import threading
_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, 'request', None)

class ThreadLocals(object):
    """
    Middleware that gets various objects from the
    request object and saves them in thread local storage.
    """
    def process_request(self, request):
        _thread_locals.request = request

按照Django文档中的说明注册该中间件

The usual aproach is to store the request object in a thread-local reference using a middleware. Then you can access this from anywhere in you app, including the Form.clean() method.

Changing the signature of the Form.clean() method means you have you own, modified version of Django, which may not be what you want.

Thank middleware count look something like this:

import threading
_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, 'request', None)

class ThreadLocals(object):
    """
    Middleware that gets various objects from the
    request object and saves them in thread local storage.
    """
    def process_request(self, request):
        _thread_locals.request = request

Register this middleware as described in the Django docs


回答 4

对于Django管理员,在Django 1.8中

class MyModelAdmin(admin.ModelAdmin):
    ...
    form = RedirectForm

    def get_form(self, request, obj=None, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        form.request = request
        return form

For Django admin, in Django 1.8

class MyModelAdmin(admin.ModelAdmin):
    ...
    form = RedirectForm

    def get_form(self, request, obj=None, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        form.request = request
        return form

回答 5

自定义管理员时遇到了这个特殊问题。我希望根据特定管理员的凭据来验证某个字段。

由于我不想修改视图以将请求作为参数传递给表单,因此我做了以下操作:

class MyCustomForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def clean(self):
        # make use of self.request here

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        def form_wrapper(*args, **kwargs):
            a = ModelForm(*args, **kwargs)
            a.request = request
            return a
    return form_wrapper

I ran into this particular problem when customizing the admin. I wanted a certain field to be validated based on the particular admin’s credentials.

Since I did not want to modify the view to pass the request as an argument to the form, the following is what I did:

class MyCustomForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def clean(self):
        # make use of self.request here

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        def form_wrapper(*args, **kwargs):
            a = ModelForm(*args, **kwargs)
            a.request = request
            return a
    return form_wrapper

回答 6

您不能总是使用此方法(及其可能的不良做法),但是如果仅在一个视图中使用表单,则可以将其范围限定在view方法本身内。

def my_view(request):

    class ResetForm(forms.Form):
        password = forms.CharField(required=True, widget=forms.PasswordInput())

        def clean_password(self):
            data = self.cleaned_data['password']
            if not request.user.check_password(data):
                raise forms.ValidationError("The password entered does not match your account password.")
            return data

    if request.method == 'POST':
        form = ResetForm(request.POST, request.FILES)
        if form.is_valid():

            return HttpResponseRedirect("/")
    else:
        form = ResetForm()

    return render_to_response(request, "reset.html")

You can’t always use this method (and its probably bad practice), but if you are only using the form in one view you could scope it inside the view method itself.

def my_view(request):

    class ResetForm(forms.Form):
        password = forms.CharField(required=True, widget=forms.PasswordInput())

        def clean_password(self):
            data = self.cleaned_data['password']
            if not request.user.check_password(data):
                raise forms.ValidationError("The password entered does not match your account password.")
            return data

    if request.method == 'POST':
        form = ResetForm(request.POST, request.FILES)
        if form.is_valid():

            return HttpResponseRedirect("/")
    else:
        form = ResetForm()

    return render_to_response(request, "reset.html")

回答 7

Daniel Roseman的答案仍然是最好的。但是,出于某些原因,我将第一个位置参数用于请求而不是关键字参数:

  1. 您不存在覆盖同名kwarg的风险
  2. 该请求是可选的,不正确。在这种情况下,request属性绝不能为None。
  3. 您可以将args和kwargs干净地传递给父类,而无需修改它们。

最后,我将使用更唯一的名称来避免覆盖现有变量。因此,我修改后的答案如下:

class MyForm(forms.Form):

  def __init__(self, request, *args, **kwargs):
      self._my_request = request
      super(MyForm, self).__init__(*args, **kwargs)


  def clean(self):
      ... access the request object via self._my_request ...

The answer by Daniel Roseman is still the best. However, I would use the first positional argument for the request instead of the keyword argument for a few reasons:

  1. You don’t run the risk of overriding a kwarg with the same name
  2. The request is optional which is not right. The request attribute should never be None in this context.
  3. You can cleanly pass the args and kwargs to the parent class without having to modify them.

Lastly, I would use a more unique name to avoid overriding an existing variable. Thus, My modified answer looks like:

class MyForm(forms.Form):

  def __init__(self, request, *args, **kwargs):
      self._my_request = request
      super(MyForm, self).__init__(*args, **kwargs)


  def clean(self):
      ... access the request object via self._my_request ...

回答 8

来自cheesebaker @ pypi的新鲜奶酪:django-requestprovider

fresh cheese from cheesebaker@pypi: django-requestprovider


回答 9

根据您的要求,我想针对该问题提供另一个答案,您希望将用户访问表单的clean方法。你可以试试看。View.py

person=User.objects.get(id=person_id)
form=MyForm(request.POST,instance=person)

表格

def __init__(self,*arg,**kwargs):
    self.instance=kwargs.get('instance',None)
    if kwargs['instance'] is not None:
        del kwargs['instance']
    super(Myform, self).__init__(*args, **kwargs)

现在您可以在form.py中以任何干净的方法访问self.instance

I have another answer to this question as per your requirement you want to access the user into the clean method of the form. You can Try this. View.py

person=User.objects.get(id=person_id)
form=MyForm(request.POST,instance=person)

forms.py

def __init__(self,*arg,**kwargs):
    self.instance=kwargs.get('instance',None)
    if kwargs['instance'] is not None:
        del kwargs['instance']
    super(Myform, self).__init__(*args, **kwargs)

Now you can access the self.instance in any clean method in form.py


回答 10

当您想通过“准备好的” Django类视图访问它时,CreateView有一个小窍门(=官方解决方案无法立即使用)。CreateView 您必须自己添加以下代码:

class MyCreateView(LoginRequiredMixin, CreateView):
    form_class = MyOwnForm
    template_name = 'my_sample_create.html'

    def get_form_kwargs(self):
        result = super().get_form_kwargs()
        result['request'] = self.request
        return result

=简而言之,这是通过requestDjango的创建/更新视图传递到表单的解决方案。

When you want to access it through “prepared” Django class views like CreateView there’s a small trick to know (= the official solution doesn’t work out of the box). In your own CreateView you’ll have to add code like this:

class MyCreateView(LoginRequiredMixin, CreateView):
    form_class = MyOwnForm
    template_name = 'my_sample_create.html'

    def get_form_kwargs(self):
        result = super().get_form_kwargs()
        result['request'] = self.request
        return result

= in short this is the solution to pass request to your form with Django’s Create/Update views.


在Django中-模型继承-是否允许您覆盖父模型的属性?

问题:在Django中-模型继承-是否允许您覆盖父模型的属性?

我正在寻找这样做:

class Place(models.Model):
   name = models.CharField(max_length=20)
   rating = models.DecimalField()

class LongNamedRestaurant(Place):  # Subclassing `Place`.
   name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
   food_type = models.CharField(max_length=25)

这是我要使用的版本(尽管我可以接受任何建议):http : //docs.djangoproject.com/en/dev/topics/db/models/#id7

Django支持吗?如果没有,有没有办法获得类似的结果?

I’m looking to do this:

class Place(models.Model):
   name = models.CharField(max_length=20)
   rating = models.DecimalField()

class LongNamedRestaurant(Place):  # Subclassing `Place`.
   name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
   food_type = models.CharField(max_length=25)

This is the version I would like to use (although I’m open to any suggestion): http://docs.djangoproject.com/en/dev/topics/db/models/#id7

Is this supported in Django? If not, is there a way to achieve similar results?


回答 0

更新的答案:正如人们在评论中指出的那样,原始答案未能正确回答问题。实际上,只有LongNamedRestaurant模型是在数据库中创建的,Place不是。

一个解决方案是创建一个代表“地方”的抽象模型。AbstractPlace,并从中继承:

class AbstractPlace(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class Place(AbstractPlace):
    pass

class LongNamedRestaurant(AbstractPlace):
    name = models.CharField(max_length=255)
    food_type = models.CharField(max_length=25)

还请阅读@Mark 答案,他给出了一个很好的解释,为什么您不能更改从非抽象类继承的属性。

(请注意,这仅在Django 1.10以后才可行:在Django 1.10之前,无法修改从抽象类继承的属性。)

原始答案

从Django 1.10开始,就有可能!您只需要做您要求的:

class Place(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class LongNamedRestaurant(Place):  # Subclassing `Place`.
    name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
    food_type = models.CharField(max_length=25)

Updated answer: as people noted in comments, the original answer wasn’t properly answering the question. Indeed, only the LongNamedRestaurant model was created in database, Place was not.

A solution is to create an abstract model representing a “Place”, eg. AbstractPlace, and inherit from it:

class AbstractPlace(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class Place(AbstractPlace):
    pass

class LongNamedRestaurant(AbstractPlace):
    name = models.CharField(max_length=255)
    food_type = models.CharField(max_length=25)

Please also read @Mark answer, he gives a great explanation why you can’t change attributes inherited from a non-abstract class.

(Note this is only possible since Django 1.10: before Django 1.10, modifying an attribute inherited from an abstract class wasn’t possible.)

Original answer

Since Django 1.10 it’s possible! You just have to do what you asked for:

class Place(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class LongNamedRestaurant(Place):  # Subclassing `Place`.
    name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
    food_type = models.CharField(max_length=25)

回答 1

不,不是

字段名称“隐藏”是不允许的

在常规的Python类继承中,子类可以覆盖父类的任何属性。在Django中,不允许将这些属性用作Field实例(至少目前是不允许的)。如果基类具有一个名为的字段author,则不能author在从该基类继承的任何类中创建另一个名为的模型字段。

No, it is not:

Field name “hiding” is not permitted

In normal Python class inheritance, it is permissible for a child class to override any attribute from the parent class. In Django, this is not permitted for attributes that are Field instances (at least, not at the moment). If a base class has a field called author, you cannot create another model field called author in any class that inherits from that base class.


回答 2

除非是抽象的,否则这是不可能的,这就是为什么:LongNamedRestaurant这也是a Place,不仅作为类,而且在数据库中。位置表包含每个纯项Place和每个项的条目LongNamedRestaurantLongNamedRestaurant只需使用创建一个额外的表格,food_type并引用该地方表格。

如果这样做Place.objects.all(),您还将获得每个是的地方,LongNamedRestaurant它将是Place(没有food_type)的实例。因此,Place.name并且LongNamedRestaurant.name共享相同的数据库列,因此必须具有相同的类型。

我认为这对于普通模特来说是有道理的:每个餐厅都是一个地方,并且至少应该拥有该地方拥有的所有东西。也许这种一致性也是为什么1.10之前的抽象模型不可能实现的原因,尽管它不会在那里带来数据库问题。如@lampslave所述,它在1.10中成为可能。我个人建议您注意:如果Sub.x覆盖Super.x,请确保Sub.x是Super.x的子类,否则Sub不能代替Super。

解决方法AUTH_USER_MODEL如果您只需要更改电子邮件字段,则可以创建一个涉及大量代码重复的自定义用户模型()。另外,您可以保留电子邮件原样,并确保所有形式的电子邮件都是必需的。如果其他应用程序使用数据库,则不能保证数据库的完整性,也不能保证数据库的完整性(如果不需要使用用户名)。

That is not possible unless abstract, and here is why: LongNamedRestaurant is also a Place, not only as a class but also in the database. The place-table contains an entry for every pure Place and for every LongNamedRestaurant. LongNamedRestaurant just creates an extra table with the food_type and a reference to the place table.

If you do Place.objects.all(), you also get every place that is a LongNamedRestaurant, and it will be an instance of Place (without the food_type). So Place.name and LongNamedRestaurant.name share the same database column, and must therefore be of the same type.

I think this makes sense for normal models: every restaurant is a place, and should have at least everything that place has. Maybe this consistency is also why it was not possible for abstract models before 1.10, although it would not give database problems there. As @lampslave remarks, it was made possible in 1.10. I would personally recommend care: if Sub.x overrides Super.x, make sure Sub.x is a subclass of Super.x, otherwise Sub cannot be used in place of Super.

Workarounds: You can create a custom user model (AUTH_USER_MODEL) which involves quite a bit of code duplication if you only need to change the email field. Alternatively you can leave email as it is and make sure it’s required in all forms. This doesn’t guarantee database integrity if other applications use it, and doesn’t work the other way around (if you want to make username not required).


回答 3

参见https://stackoverflow.com/a/6379556/15690

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

class Message(BaseMessage):
    # some fields...
Message._meta.get_field('is_public').default = True

See https://stackoverflow.com/a/6379556/15690:

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

class Message(BaseMessage):
    # some fields...
Message._meta.get_field('is_public').default = True

回答 4

将您的代码粘贴到新的应用程序中,将应用程序添加到INSTALLED_APPS并运行syncdb:

django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'

看起来Django不支持该功能。

Pasted your code into a fresh app, added app to INSTALLED_APPS and ran syncdb:

django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'

Looks like Django does not support that.


回答 5

这段超酷的代码段使您可以“覆盖”抽象父类中的字段。

def AbstractClassWithoutFieldsNamed(cls, *excl):
    """
    Removes unwanted fields from abstract base classes.

    Usage::
    >>> from oscar.apps.address.abstract_models import AbstractBillingAddress

    >>> from koe.meta import AbstractClassWithoutFieldsNamed as without
    >>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
    ...     pass
    """
    if cls._meta.abstract:
        remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
        for f in remove_fields:
            cls._meta.local_fields.remove(f)
        return cls
    else:
        raise Exception("Not an abstract model")

从抽象父类中删除字段后,您可以根据需要重新定义它们。

这不是我自己的工作。来自此处的原始代码:https : //gist.github.com/specialunderwear/9d917ddacf3547b646ba

This supercool piece of code allows you to ‘override’ fields in abstract parent classes.

def AbstractClassWithoutFieldsNamed(cls, *excl):
    """
    Removes unwanted fields from abstract base classes.

    Usage::
    >>> from oscar.apps.address.abstract_models import AbstractBillingAddress

    >>> from koe.meta import AbstractClassWithoutFieldsNamed as without
    >>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
    ...     pass
    """
    if cls._meta.abstract:
        remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
        for f in remove_fields:
            cls._meta.local_fields.remove(f)
        return cls
    else:
        raise Exception("Not an abstract model")

When the fields have been removed from the abstract parent class you are free to redefine them as you need.

This is not my own work. Original code from here: https://gist.github.com/specialunderwear/9d917ddacf3547b646ba


回答 6

也许您可以处理contribute_to_class:

class LongNamedRestaurant(Place):

    food_type = models.CharField(max_length=25)

    def __init__(self, *args, **kwargs):
        super(LongNamedRestaurant, self).__init__(*args, **kwargs)
        name = models.CharField(max_length=255)
        name.contribute_to_class(self, 'name')

Syncdb正常工作。我没有尝试过这个例子,就我而言,我只是重写了一个约束参数,所以… wait&see!

Maybe you could deal with contribute_to_class :

class LongNamedRestaurant(Place):

    food_type = models.CharField(max_length=25)

    def __init__(self, *args, **kwargs):
        super(LongNamedRestaurant, self).__init__(*args, **kwargs)
        name = models.CharField(max_length=255)
        name.contribute_to_class(self, 'name')

Syncdb works fine. I dont tried this example, in my case I just override a constraint parameter so … wait & see !


回答 7

我知道这是一个老问题,但是我遇到了类似的问题,并找到了解决方法:

我有以下类:

class CommonInfo(models.Model):
    image = models.ImageField(blank=True, null=True, default="")

    class Meta:
        abstract = True

class Year(CommonInfo):
    year = models.IntegerField() 

但是我希望在保留超类的图像字段为空的同时,需要Year的继承图像字段。最后,我在验证阶段使用了ModelForms来执行图像:

class YearForm(ModelForm):
    class Meta:
        model = Year

    def clean(self):
        if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
            raise ValidationError("Please provide an image.")

        return self.cleaned_data

admin.py:

class YearAdmin(admin.ModelAdmin):
    form = YearForm

看来这仅适用于某些情况(肯定需要在子类字段上执行更严格的规则)。

或者,您可以使用clean_<fieldname>()方法代替clean(),例如,如果town需要填写字段:

def clean_town(self):
    town = self.cleaned_data["town"]
    if not town or len(town) == 0:
        raise forms.ValidationError("Please enter a town")
    return town

I know it’s an old question, but i had a similar problem and found a workaround:

I had the following classes:

class CommonInfo(models.Model):
    image = models.ImageField(blank=True, null=True, default="")

    class Meta:
        abstract = True

class Year(CommonInfo):
    year = models.IntegerField() 

But I wanted Year’s inherited image-field to be required while keeping the image field of the superclass nullable. In the end I used ModelForms to enforce the image at the validation stage:

class YearForm(ModelForm):
    class Meta:
        model = Year

    def clean(self):
        if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
            raise ValidationError("Please provide an image.")

        return self.cleaned_data

admin.py:

class YearAdmin(admin.ModelAdmin):
    form = YearForm

It seems this is only applicable for some situations (certainly where you need to enforce stricter rules on the subclass field).

Alternatively you can use the clean_<fieldname>() method instead of clean(), e.g. if a field town would be required to be filled in:

def clean_town(self):
    town = self.cleaned_data["town"]
    if not town or len(town) == 0:
        raise forms.ValidationError("Please enter a town")
    return town

回答 8

您不能覆盖Model字段,但是可以通过覆盖/指定clean()方法轻松实现。我遇到了与电子邮件字段有关的问题,并希望使其在模型级别具有唯一性,并且这样做是这样的:

def clean(self):
    """
    Make sure that email field is unique
    """
    if MyUser.objects.filter(email=self.email):
        raise ValidationError({'email': _('This email is already in use')})

然后,错误消息将被名称为“ email”的表单字段捕获。

You can not override Model fields, but its easily achieved by overriding/specifying clean() method. I had the issue with email field and wanted to make it unique on Model level and did it like this:

def clean(self):
    """
    Make sure that email field is unique
    """
    if MyUser.objects.filter(email=self.email):
        raise ValidationError({'email': _('This email is already in use')})

The error message is then captured by Form field with name “email”


回答 9

我的解决方案与next一样简单monkey patching,请注意如何更改模型中的field max_length属性:nameLongNamedRestaurant

class Place(models.Model):
   name = models.CharField(max_length=20)

class LongNamedRestaurant(Place):
    food_type = models.CharField(max_length=25)
    Place._meta.get_field('name').max_length = 255

My solution is as simple as next monkey patching, notice how I changed max_length attribute fo name field in LongNamedRestaurant model:

class Place(models.Model):
   name = models.CharField(max_length=20)

class LongNamedRestaurant(Place):
    food_type = models.CharField(max_length=25)
    Place._meta.get_field('name').max_length = 255

如何以相同的顺序比较两个具有相同元素的JSON对象相等?

问题:如何以相同的顺序比较两个具有相同元素的JSON对象相等?

我如何测试python中两个JSON对象是否相等,而忽略列表的顺序?

例如 …

JSON文件a

{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}

JSON文档b

{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}

a并且b应该比较相等,即使"errors"列表的顺序不同。

How can I test whether two JSON objects are equal in python, disregarding the order of lists?

For example …

JSON document a:

{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}

JSON document b:

{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}

a and b should compare equal, even though the order of the "errors" lists are different.


回答 0

如果您希望两个具有相同元素但顺序不同的对象相等,那么比较明显的事情就是比较它们的排序后的副本-例如,以JSON字符串a和表示的字典b

import json

a = json.loads("""
{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}
""")

b = json.loads("""
{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}
""")
>>> sorted(a.items()) == sorted(b.items())
False

…但这是行不通的,因为在每种情况下,"errors"顶层dict的项都是一个列表,其中相同元素的顺序不同,并且sorted()除“一个可迭代的。

为了解决这个问题,我们可以定义一个ordered函数,该函数将对找到的所有列表进行递归排序(并将字典转换(key, value)成对列表,以便它们可排序):

def ordered(obj):
    if isinstance(obj, dict):
        return sorted((k, ordered(v)) for k, v in obj.items())
    if isinstance(obj, list):
        return sorted(ordered(x) for x in obj)
    else:
        return obj

如果我们将此功能应用于ab,结果比较相等:

>>> ordered(a) == ordered(b)
True

If you want two objects with the same elements but in a different order to compare equal, then the obvious thing to do is compare sorted copies of them – for instance, for the dictionaries represented by your JSON strings a and b:

import json

a = json.loads("""
{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}
""")

b = json.loads("""
{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}
""")
>>> sorted(a.items()) == sorted(b.items())
False

… but that doesn’t work, because in each case, the "errors" item of the top-level dict is a list with the same elements in a different order, and sorted() doesn’t try to sort anything except the “top” level of an iterable.

To fix that, we can define an ordered function which will recursively sort any lists it finds (and convert dictionaries to lists of (key, value) pairs so that they’re orderable):

def ordered(obj):
    if isinstance(obj, dict):
        return sorted((k, ordered(v)) for k, v in obj.items())
    if isinstance(obj, list):
        return sorted(ordered(x) for x in obj)
    else:
        return obj

If we apply this function to a and b, the results compare equal:

>>> ordered(a) == ordered(b)
True

回答 1

另一种方法是使用json.dumps(X, sort_keys=True)选项:

import json
a, b = json.dumps(a, sort_keys=True), json.dumps(b, sort_keys=True)
a == b # a normal string comparison

这适用于嵌套字典和列表。

Another way could be to use json.dumps(X, sort_keys=True) option:

import json
a, b = json.dumps(a, sort_keys=True), json.dumps(b, sort_keys=True)
a == b # a normal string comparison

This works for nested dictionaries and lists.


回答 2

对其进行解码,并将其作为mgilson注释进行比较。

字典的顺序无关紧要,只要键和值匹配即可。(字典在Python中没有顺序)

>>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1}
True

但是顺序在清单中很重要。排序将解决列表的问题。

>>> [1, 2] == [2, 1]
False
>>> [1, 2] == sorted([2, 1])
True

>>> a = '{"errors": [{"error": "invalid", "field": "email"}, {"error": "required", "field": "name"}], "success": false}'
>>> b = '{"errors": [{"error": "required", "field": "name"}, {"error": "invalid", "field": "email"}], "success": false}'
>>> a, b = json.loads(a), json.loads(b)
>>> a['errors'].sort()
>>> b['errors'].sort()
>>> a == b
True

上面的示例适用于问题中的JSON。有关一般解决方案,请参见Zero Piraeus的答案。

Decode them and compare them as mgilson comment.

Order does not matter for dictionary as long as the keys, and values matches. (Dictionary has no order in Python)

>>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1}
True

But order is important in list; sorting will solve the problem for the lists.

>>> [1, 2] == [2, 1]
False
>>> [1, 2] == sorted([2, 1])
True

>>> a = '{"errors": [{"error": "invalid", "field": "email"}, {"error": "required", "field": "name"}], "success": false}'
>>> b = '{"errors": [{"error": "required", "field": "name"}, {"error": "invalid", "field": "email"}], "success": false}'
>>> a, b = json.loads(a), json.loads(b)
>>> a['errors'].sort()
>>> b['errors'].sort()
>>> a == b
True

Above example will work for the JSON in the question. For general solution, see Zero Piraeus’s answer.


回答 3

对于以下两个字典“ dictWithListsInValue”和“ reorderedDictWithReorderedListsInValue”,它们只是彼此的重新排序版本

dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {'A': [{'X': [dictObj2, dictObj]}, {'Y': 2}], 'B': dictObj2}
reorderedDictWithReorderedListsInValue = {'B': dictObj2, 'A': [{'Y': 2}, {'X': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}

print(sorted(a.items()) == sorted(b.items()))  # gives false

给我错误的结果即错误。

所以我这样创建了自己的cutstom ObjectComparator:

def my_list_cmp(list1, list2):
    if (list1.__len__() != list2.__len__()):
        return False

    for l in list1:
        found = False
        for m in list2:
            res = my_obj_cmp(l, m)
            if (res):
                found = True
                break

        if (not found):
            return False

    return True


def my_obj_cmp(obj1, obj2):
    if isinstance(obj1, list):
        if (not isinstance(obj2, list)):
            return False
        return my_list_cmp(obj1, obj2)
    elif (isinstance(obj1, dict)):
        if (not isinstance(obj2, dict)):
            return False
        exp = set(obj2.keys()) == set(obj1.keys())
        if (not exp):
            # print(obj1.keys(), obj2.keys())
            return False
        for k in obj1.keys():
            val1 = obj1.get(k)
            val2 = obj2.get(k)
            if isinstance(val1, list):
                if (not my_list_cmp(val1, val2)):
                    return False
            elif isinstance(val1, dict):
                if (not my_obj_cmp(val1, val2)):
                    return False
            else:
                if val2 != val1:
                    return False
    else:
        return obj1 == obj2

    return True


dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {'A': [{'X': [dictObj2, dictObj]}, {'Y': 2}], 'B': dictObj2}
reorderedDictWithReorderedListsInValue = {'B': dictObj2, 'A': [{'Y': 2}, {'X': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}

print(my_obj_cmp(a, b))  # gives true

这给了我正确的预期输出!

逻辑很简单:

如果对象的类型为“列表”,则将第一个列表的每个项目与第二个列表的项目进行比较,直到找到为止;如果在通过第二个列表之后未找到该项目,则“找到”为= false。返回“找到的”值

否则,如果要比较的对象的类型为“ dict”,则比较两个对象中所有相应键的存在值。(执行递归比较)

否则,只需调用obj1 == obj2即可。默认情况下,它适用于字符串和数字的对象,并且eq()的定义适当。

(请注意,可以通过删除在object2中找到的项目来进一步改进该算法,以便object1的下一个项目不会将自身与object2中已经找到的项目进行比较。)

For the following two dicts ‘dictWithListsInValue’ and ‘reorderedDictWithReorderedListsInValue’ which are simply reordered versions of each other

dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {'A': [{'X': [dictObj2, dictObj]}, {'Y': 2}], 'B': dictObj2}
reorderedDictWithReorderedListsInValue = {'B': dictObj2, 'A': [{'Y': 2}, {'X': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}

print(sorted(a.items()) == sorted(b.items()))  # gives false

gave me wrong result i.e. false .

So I created my own cutstom ObjectComparator like this:

def my_list_cmp(list1, list2):
    if (list1.__len__() != list2.__len__()):
        return False

    for l in list1:
        found = False
        for m in list2:
            res = my_obj_cmp(l, m)
            if (res):
                found = True
                break

        if (not found):
            return False

    return True


def my_obj_cmp(obj1, obj2):
    if isinstance(obj1, list):
        if (not isinstance(obj2, list)):
            return False
        return my_list_cmp(obj1, obj2)
    elif (isinstance(obj1, dict)):
        if (not isinstance(obj2, dict)):
            return False
        exp = set(obj2.keys()) == set(obj1.keys())
        if (not exp):
            # print(obj1.keys(), obj2.keys())
            return False
        for k in obj1.keys():
            val1 = obj1.get(k)
            val2 = obj2.get(k)
            if isinstance(val1, list):
                if (not my_list_cmp(val1, val2)):
                    return False
            elif isinstance(val1, dict):
                if (not my_obj_cmp(val1, val2)):
                    return False
            else:
                if val2 != val1:
                    return False
    else:
        return obj1 == obj2

    return True


dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {'A': [{'X': [dictObj2, dictObj]}, {'Y': 2}], 'B': dictObj2}
reorderedDictWithReorderedListsInValue = {'B': dictObj2, 'A': [{'Y': 2}, {'X': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}

print(my_obj_cmp(a, b))  # gives true

which gave me the correct expected output!

Logic is pretty simple:

If the objects are of type ‘list’ then compare each item of the first list with the items of the second list until found , and if the item is not found after going through the second list , then ‘found’ would be = false. ‘found’ value is returned

Else if the objects to be compared are of type ‘dict’ then compare the values present for all the respective keys in both the objects. (Recursive comparison is performed)

Else simply call obj1 == obj2 . It by default works fine for the object of strings and numbers and for those eq() is defined appropriately .

(Note that the algorithm can further be improved by removing the items found in object2, so that the next item of object1 would not compare itself with the items already found in the object2)


回答 4

您可以编写自己的equals函数:

  • 在以下情况下,字典是相等的:1)所有键都相等,2)所有值都相等
  • 如果满足以下条件,则列表相等:所有项目均相同且顺序相同
  • 如果原语相等 a == b

因为您处理JSON,你就会有标准的Python类型:dictlist等等,所以你可以做硬类型检查if type(obj) == 'dict':,等等。

粗略示例(未经测试):

def json_equals(jsonA, jsonB):
    if type(jsonA) != type(jsonB):
        # not equal
        return False
    if type(jsonA) == dict:
        if len(jsonA) != len(jsonB):
            return False
        for keyA in jsonA:
            if keyA not in jsonB or not json_equal(jsonA[keyA], jsonB[keyA]):
                return False
    elif type(jsonA) == list:
        if len(jsonA) != len(jsonB):
            return False
        for itemA, itemB in zip(jsonA, jsonB):
            if not json_equal(itemA, itemB):
                return False
    else:
        return jsonA == jsonB

You can write your own equals function:

  • dicts are equal if: 1) all keys are equal, 2) all values are equal
  • lists are equal if: all items are equal and in the same order
  • primitives are equal if a == b

Because you’re dealing with json, you’ll have standard python types: dict, list, etc., so you can do hard type checking if type(obj) == 'dict':, etc.

Rough example (not tested):

def json_equals(jsonA, jsonB):
    if type(jsonA) != type(jsonB):
        # not equal
        return False
    if type(jsonA) == dict:
        if len(jsonA) != len(jsonB):
            return False
        for keyA in jsonA:
            if keyA not in jsonB or not json_equal(jsonA[keyA], jsonB[keyA]):
                return False
    elif type(jsonA) == list:
        if len(jsonA) != len(jsonB):
            return False
        for itemA, itemB in zip(jsonA, jsonB):
            if not json_equal(itemA, itemB):
                return False
    else:
        return jsonA == jsonB

回答 5

对于其他想要调试两个JSON对象(通常有一个引用和一个target)的人,可以使用以下解决方案。它将列出从目标到引用的不同/不匹配路径的“ 路径 ”。

level 选项用于选择您要研究的深度。

show_variables 可以打开该选项以显示相关变量。

def compareJson(example_json, target_json, level=-1, show_variables=False):
  _different_variables = _parseJSON(example_json, target_json, level=level, show_variables=show_variables)
  return len(_different_variables) == 0, _different_variables

def _parseJSON(reference, target, path=[], level=-1, show_variables=False):  
  if level > 0 and len(path) == level:
    return []
  
  _different_variables = list()
  # the case that the inputs is a dict (i.e. json dict)  
  if isinstance(reference, dict):
    for _key in reference:      
      _path = path+[_key]
      try:
        _different_variables += _parseJSON(reference[_key], target[_key], _path, level, show_variables)
      except KeyError:
        _record = ''.join(['[%s]'%str(p) for p in _path])
        if show_variables:
          _record += ': %s <--> MISSING!!'%str(reference[_key])
        _different_variables.append(_record)
  # the case that the inputs is a list/tuple
  elif isinstance(reference, list) or isinstance(reference, tuple):
    for index, v in enumerate(reference):
      _path = path+[index]
      try:
        _target_v = target[index]
        _different_variables += _parseJSON(v, _target_v, _path, level, show_variables)
      except IndexError:
        _record = ''.join(['[%s]'%str(p) for p in _path])
        if show_variables:
          _record += ': %s <--> MISSING!!'%str(v)
        _different_variables.append(_record)
  # the actual comparison about the value, if they are not the same, record it
  elif reference != target:
    _record = ''.join(['[%s]'%str(p) for p in path])
    if show_variables:
      _record += ': %s <--> %s'%(str(reference), str(target))
    _different_variables.append(_record)

  return _different_variables

For others who’d like to debug the two JSON objects (usually, there is a reference and a target), here is a solution you may use. It will list the “path” of different/mismatched ones from target to the reference.

level option is used for selecting how deep you would like to look into.

show_variables option can be turned on to show the relevant variable.

def compareJson(example_json, target_json, level=-1, show_variables=False):
  _different_variables = _parseJSON(example_json, target_json, level=level, show_variables=show_variables)
  return len(_different_variables) == 0, _different_variables

def _parseJSON(reference, target, path=[], level=-1, show_variables=False):  
  if level > 0 and len(path) == level:
    return []
  
  _different_variables = list()
  # the case that the inputs is a dict (i.e. json dict)  
  if isinstance(reference, dict):
    for _key in reference:      
      _path = path+[_key]
      try:
        _different_variables += _parseJSON(reference[_key], target[_key], _path, level, show_variables)
      except KeyError:
        _record = ''.join(['[%s]'%str(p) for p in _path])
        if show_variables:
          _record += ': %s <--> MISSING!!'%str(reference[_key])
        _different_variables.append(_record)
  # the case that the inputs is a list/tuple
  elif isinstance(reference, list) or isinstance(reference, tuple):
    for index, v in enumerate(reference):
      _path = path+[index]
      try:
        _target_v = target[index]
        _different_variables += _parseJSON(v, _target_v, _path, level, show_variables)
      except IndexError:
        _record = ''.join(['[%s]'%str(p) for p in _path])
        if show_variables:
          _record += ': %s <--> MISSING!!'%str(v)
        _different_variables.append(_record)
  # the actual comparison about the value, if they are not the same, record it
  elif reference != target:
    _record = ''.join(['[%s]'%str(p) for p in path])
    if show_variables:
      _record += ': %s <--> %s'%(str(reference), str(target))
    _different_variables.append(_record)

  return _different_variables

如何在Django REST Framework上启用CORS

问题:如何在Django REST Framework上启用CORS

如何在Django REST框架上启用CORS?该参考没有太大帮助,它说我可以通过中间件来完成,但是我该怎么做呢?

How can I enable CORS on my Django REST Framework? the reference doesn’t help much, it says that I can do by a middleware, but how can I do that?


回答 0

您在问题中引用的链接建议使用django-cors-headers,其文档指出要安装该库

pip install django-cors-headers

然后将其添加到已安装的应用程序中:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

您还需要添加一个中间件类来侦听响应:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
)

请浏览其文档的配置部分,并特别注意各种CORS_ORIGIN_设置。您需要根据需要设置其中一些。

The link you referenced in your question recommends using django-cors-headers, whose documentation says to install the library

pip install django-cors-headers

and then add it to your installed apps:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

You will also need to add a middleware class to listen in on responses:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
)

Please browse the configuration section of its documentation, paying particular attention to the various CORS_ORIGIN_ settings. You’ll need to set some of those based on your needs.


回答 1

pip install django-cors-headers

然后将其添加到已安装的应用程序中:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

您还需要添加一个中间件类来侦听响应:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',  
    'django.middleware.common.CommonMiddleware',  
    ...
)

CORS_ORIGIN_ALLOW_ALL = True # If this is used then `CORS_ORIGIN_WHITELIST` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = [
    'http://localhost:3030',
] # If this is used, then not need to use `CORS_ORIGIN_ALLOW_ALL = True`
CORS_ORIGIN_REGEX_WHITELIST = [
    'http://localhost:3030',
]

更多详细信息:https : //github.com/ottoyiu/django-cors-headers/#configuration

阅读官方文档可以解决几乎所有问题

pip install django-cors-headers

and then add it to your installed apps:

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

You will also need to add a middleware class to listen in on responses:

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',  
    'django.middleware.common.CommonMiddleware',  
    ...
)

CORS_ORIGIN_ALLOW_ALL = True # If this is used then `CORS_ORIGIN_WHITELIST` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = [
    'http://localhost:3030',
] # If this is used, then not need to use `CORS_ORIGIN_ALLOW_ALL = True`
CORS_ORIGIN_REGEX_WHITELIST = [
    'http://localhost:3030',
]

more details: https://github.com/ottoyiu/django-cors-headers/#configuration

read the official documentation can resolve almost all problem


回答 2

即使知道最佳选择是使用经过测试的包方法,您也可以使用自定义中间件来进行操作django-cors-headers。这样说,这里是解决方案:

创建以下结构和文件:

myapp/middleware/__init__.py

from corsMiddleware import corsMiddleware

myapp/middleware/corsMiddleware.py

class corsMiddleware(object):
    def process_response(self, req, resp):
        resp["Access-Control-Allow-Origin"] = "*"
        return resp

添加到settings.py标记的行:

MIDDLEWARE_CLASSES = (
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",

    # Now we add here our custom middleware
     'app_name.middleware.corsMiddleware' <---- this line
)

You can do by using a custom middleware, even though knowing that the best option is using the tested approach of the package django-cors-headers. With that said, here is the solution:

create the following structure and files:

myapp/middleware/__init__.py

from corsMiddleware import corsMiddleware

myapp/middleware/corsMiddleware.py

class corsMiddleware(object):
    def process_response(self, req, resp):
        resp["Access-Control-Allow-Origin"] = "*"
        return resp

add to settings.py the marked line:

MIDDLEWARE_CLASSES = (
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",

    # Now we add here our custom middleware
     'app_name.middleware.corsMiddleware' <---- this line
)

回答 3

万一有人回到这个问题并决定编写自己的中间件,这是Django新型中间件的代码示例-

class CORSMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"

        return response

In case anyone is getting back to this question and deciding to write their own middleware, this is a code sample for Django’s new style middleware –

class CORSMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"

        return response

回答 4

根据文档,对于Django版本> 1.10,可以将自定义MIDDLEWARE编写为函数,例如在文件中:(yourproject/middleware.py作为的同级settings.py):

def open_access_middleware(get_response):
    def middleware(request):
        response = get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"
        return response
    return middleware

最后,将此函数的python路径(写入项目的根目录)添加到项目的MIDDLEWARE列表中settings.py

MIDDLEWARE = [
  .
  .
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
  'yourproject.middleware.open_access_middleware'
]

十分简单!

For Django versions > 1.10, according to the documentation, a custom MIDDLEWARE can be written as a function, let’s say in the file: yourproject/middleware.py (as a sibling of settings.py):

def open_access_middleware(get_response):
    def middleware(request):
        response = get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"
        return response
    return middleware

and finally, add the python path of this function (w.r.t. the root of your project) to the MIDDLEWARE list in your project’s settings.py:

MIDDLEWARE = [
  .
  .
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
  'yourproject.middleware.open_access_middleware'
]

Easy peasy!


回答 5

好吧,我不认识男人,但是:

在这里使用python 3.6和django 2.2

在settings.py中将MIDDLEWARE_CLASSES重命名为MIDDLEWARE起作用。

Well, I don’t know guys but:

using here python 3.6 and django 2.2

Renaming MIDDLEWARE_CLASSES to MIDDLEWARE in settings.py worked.


回答 6

以下是不需要任何外部模块的工作步骤:

步骤1:在您的应用中创建一个模块。

例如,假设我们有一个名为user_registration_app的应用程序。探索user_registration_app并创建一个新文件。

让我们将其称为custom_cors_middleware.py

粘贴下面的类定义:

class CustomCorsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"

        # Code to be executed for each request/response after
        # the view is called.

        return response

步骤2:注册中间件

在您的项目settings.py文件中,添加以下行

‘user_registration_app.custom_cors_middleware.CustomCorsMiddleware’

例如:

  MIDDLEWARE = [
        'user_registration_app.custom_cors_middleware.CustomCorsMiddleware', # ADD THIS LINE BEFORE CommonMiddleware
         ...
        'django.middleware.common.CommonMiddleware',

    ]

请记住,将user_registration_app替换为在其中创建了custom_cors_middleware.py模块的应用程序的名称。

现在,您可以验证它会将必需的响应标头添加到项目中的所有视图中!

Below are the working steps without the need for any external modules:

Step 1: Create a module in your app.

E.g, lets assume we have an app called user_registration_app. Explore user_registration_app and create a new file.

Lets call this as custom_cors_middleware.py

Paste the below Class definition:

class CustomCorsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)
        response["Access-Control-Allow-Origin"] = "*"
        response["Access-Control-Allow-Headers"] = "*"

        # Code to be executed for each request/response after
        # the view is called.

        return response

Step 2: Register a middleware

In your projects settings.py file, add this line

‘user_registration_app.custom_cors_middleware.CustomCorsMiddleware’

E.g:

  MIDDLEWARE = [
        'user_registration_app.custom_cors_middleware.CustomCorsMiddleware', # ADD THIS LINE BEFORE CommonMiddleware
         ...
        'django.middleware.common.CommonMiddleware',

    ]

Remember to replace user_registration_app with the name of your app where you have created your custom_cors_middleware.py module.

You can now verify it will add the required response headers to all the views in the project!


回答 7

Django = 2.2.12 django-cors-headers = 3.2.1 djangorestframework = 3.11.0

遵循官方指示无效

最后使用旧的方法来解决。

加:

# proj/middlewares.py
from rest_framework.authentication import SessionAuthentication


class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening
#proj/settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'proj.middlewares.CsrfExemptSessionAuthentication',
    ),
}

Django=2.2.12 django-cors-headers=3.2.1 djangorestframework=3.11.0

Follow the official instruction doesn’t work

Finally use the old way to figure it out.

ADD:

# proj/middlewares.py
from rest_framework.authentication import SessionAuthentication


class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening

#proj/settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'proj.middlewares.CsrfExemptSessionAuthentication',
    ),
}

如何限制Django模型中数字字段的最大值?

问题:如何限制Django模型中数字字段的最大值?

Django有各种可用于模型的数字字段,例如DecimalFieldPositiveIntegerField。尽管前者可以限制为存储的小数位数和总字符数,但是有没有办法将其限制为存储一定范围内的数字,例如0.0-5.0?

失败了,有什么方法可以限制PositiveIntegerField只存储例如最大为50的数字吗?

更新:现在,错误6845 已关闭,此StackOverflow问题可能没有意义。-sampablokuper

Django has various numeric fields available for use in models, e.g. DecimalField and PositiveIntegerField. Although the former can be restricted to the number of decimal places stored and the overall number of characters stored, is there any way to restrict it to storing only numbers within a certain range, e.g. 0.0-5.0 ?

Failing that, is there any way to restrict a PositiveIntegerField to only store, for instance, numbers up to 50?

Update: now that Bug 6845 has been closed, this StackOverflow question may be moot. – sampablokuper


回答 0

您还可以创建自定义模型字段类型-请参见http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

在这种情况下,您可以从内置的IntegerField中“继承”并覆盖其验证逻辑。

我考虑得越多,我意识到这对于许多Django应用程序将很有用。也许IntegerRangeField类型可以作为补丁提交,供Django开发人员考虑添加到主干。

这为我工作:

from django.db import models

class IntegerRangeField(models.IntegerField):
    def __init__(self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs):
        self.min_value, self.max_value = min_value, max_value
        models.IntegerField.__init__(self, verbose_name, name, **kwargs)
    def formfield(self, **kwargs):
        defaults = {'min_value': self.min_value, 'max_value':self.max_value}
        defaults.update(kwargs)
        return super(IntegerRangeField, self).formfield(**defaults)

然后,在模型类中,您将像这样使用它(字段是放置上述代码的模块):

size = fields.IntegerRangeField(min_value=1, max_value=50)

对于一个负值和正值范围(例如振荡器范围)进行“或”操作:

size = fields.IntegerRangeField(min_value=-100, max_value=100)

如果可以用范围运算符这样调用它,那将是一件很酷的事情:

size = fields.IntegerRangeField(range(1, 50))

但是,这将需要更多代码,因为您可以指定“跳过”参数-range(1,50,2)-有趣的主意…

You could also create a custom model field type – see http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

In this case, you could ‘inherit’ from the built-in IntegerField and override its validation logic.

The more I think about this, I realize how useful this would be for many Django apps. Perhaps a IntegerRangeField type could be submitted as a patch for the Django devs to consider adding to trunk.

This is working for me:

from django.db import models

class IntegerRangeField(models.IntegerField):
    def __init__(self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs):
        self.min_value, self.max_value = min_value, max_value
        models.IntegerField.__init__(self, verbose_name, name, **kwargs)
    def formfield(self, **kwargs):
        defaults = {'min_value': self.min_value, 'max_value':self.max_value}
        defaults.update(kwargs)
        return super(IntegerRangeField, self).formfield(**defaults)

Then in your model class, you would use it like this (field being the module where you put the above code):

size = fields.IntegerRangeField(min_value=1, max_value=50)

OR for a range of negative and positive (like an oscillator range):

size = fields.IntegerRangeField(min_value=-100, max_value=100)

What would be really cool is if it could be called with the range operator like this:

size = fields.IntegerRangeField(range(1, 50))

But, that would require a lot more code since since you can specify a ‘skip’ parameter – range(1, 50, 2) – Interesting idea though…


回答 1

您可以使用Django的内置验证器

from django.db.models import IntegerField, Model
from django.core.validators import MaxValueValidator, MinValueValidator

class CoolModelBro(Model):
    limited_integer_field = IntegerField(
        default=1,
        validators=[
            MaxValueValidator(100),
            MinValueValidator(1)
        ]
     )

编辑:直接使用模型时,请确保在保存模型之前调用模型full_clean方法以触发验证器。使用ModelForm表格时不需要这样做,因为表格会自动执行。

You can use Django’s built-in validators

from django.db.models import IntegerField, Model
from django.core.validators import MaxValueValidator, MinValueValidator

class CoolModelBro(Model):
    limited_integer_field = IntegerField(
        default=1,
        validators=[
            MaxValueValidator(100),
            MinValueValidator(1)
        ]
     )

Edit: When working directly with the model, make sure to call the model full_clean method before saving the model in order to trigger the validators. This is not required when using ModelForm since the forms will do that automatically.


回答 2

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(validators=[MinValueValidator(0),
                                       MaxValueValidator(5)])
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(validators=[MinValueValidator(0),
                                       MaxValueValidator(5)])

回答 3

我有同样的问题。这是我的解决方案:

SCORE_CHOICES = zip( range(1,n), range(1,n) )
score = models.IntegerField(choices=SCORE_CHOICES, blank=True)

I had this very same problem; here was my solution:

SCORE_CHOICES = zip( range(1,n), range(1,n) )
score = models.IntegerField(choices=SCORE_CHOICES, blank=True)

回答 4

有两种方法可以做到这一点。一种是使用表单验证,永远不要让用户输入超过50个数字。表单验证文档

如果该过程中没有用户参与,或者您没有使用表单输入数据,那么您将不得不重写模型的save方法以引发异常或限制进入该字段的数据。

There are two ways to do this. One is to use form validation to never let any number over 50 be entered by a user. Form validation docs.

If there is no user involved in the process, or you’re not using a form to enter data, then you’ll have to override the model’s save method to throw an exception or limit the data going into the field.


回答 5

如果您想要一些额外的灵活性并且不想更改模型字段,这是最好的解决方案。只需添加此自定义验证器:

#Imports
from django.core.exceptions import ValidationError      

class validate_range_or_null(object):
    compare = lambda self, a, b, c: a > c or a < b
    clean = lambda self, x: x
    message = ('Ensure this value is between %(limit_min)s and %(limit_max)s (it is %(show_value)s).')
    code = 'limit_value'

    def __init__(self, limit_min, limit_max):
        self.limit_min = limit_min
        self.limit_max = limit_max

    def __call__(self, value):
        cleaned = self.clean(value)
        params = {'limit_min': self.limit_min, 'limit_max': self.limit_max, 'show_value': cleaned}
        if value:  # make it optional, remove it to make required, or make required on the model
            if self.compare(cleaned, self.limit_min, self.limit_max):
                raise ValidationError(self.message, code=self.code, params=params)

它可以这样使用:

class YourModel(models.Model):

    ....
    no_dependents = models.PositiveSmallIntegerField("How many dependants?", blank=True, null=True, default=0, validators=[validate_range_or_null(1,100)])

两个参数是max和min,它允许为空。您可以根据需要通过删除标记的if语句来自定义验证器,或者在模型中将字段更改为blank = False,null = False。当然,这将需要迁移。

注意:我必须添加验证器,因为Django不会在PositiveSmallIntegerField上验证范围,而是为该字段创建一个smallint(在postgres中),并且如果指定的数字超出范围,则会出现DB错误。

希望这会有所帮助:)有关Django中验证程序的更多信息

PS。我的答案基于django.core.validators中的BaseValidator,但除代码外,其他所有内容都不同。

Here is the best solution if you want some extra flexibility and don’t want to change your model field. Just add this custom validator:

#Imports
from django.core.exceptions import ValidationError      

class validate_range_or_null(object):
    compare = lambda self, a, b, c: a > c or a < b
    clean = lambda self, x: x
    message = ('Ensure this value is between %(limit_min)s and %(limit_max)s (it is %(show_value)s).')
    code = 'limit_value'

    def __init__(self, limit_min, limit_max):
        self.limit_min = limit_min
        self.limit_max = limit_max

    def __call__(self, value):
        cleaned = self.clean(value)
        params = {'limit_min': self.limit_min, 'limit_max': self.limit_max, 'show_value': cleaned}
        if value:  # make it optional, remove it to make required, or make required on the model
            if self.compare(cleaned, self.limit_min, self.limit_max):
                raise ValidationError(self.message, code=self.code, params=params)

And it can be used as such:

class YourModel(models.Model):

    ....
    no_dependents = models.PositiveSmallIntegerField("How many dependants?", blank=True, null=True, default=0, validators=[validate_range_or_null(1,100)])

The two parameters are max and min, and it allows nulls. You can customize the validator if you like by getting rid of the marked if statement or change your field to be blank=False, null=False in the model. That will of course require a migration.

Note: I had to add the validator because Django does not validate the range on PositiveSmallIntegerField, instead it creates a smallint (in postgres) for this field and you get a DB error if the numeric specified is out of range.

Hope this helps :) More on Validators in Django.

PS. I based my answer on BaseValidator in django.core.validators, but everything is different except for the code.