标签归档:Django

在Python Django中运行单元测试时,如何禁用日志记录?

问题:在Python Django中运行单元测试时,如何禁用日志记录?

我正在使用一个基于单元测试的简单测试运行器来测试我的Django应用程序。

我的应用程序本身配置为在settings.py中使用基本记录器,方法是:

logging.basicConfig(level=logging.DEBUG)

在我的应用程序代码中使用:

logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))

但是,在运行单元测试时,我想禁用日志记录,以免混乱我的测试结果输出。有没有一种简单的方法可以以全局方式关闭日志记录,以便在运行测试时,特定于应用程序的记录器不会将内容写到控制台上?

I am using a simple unit test based test runner to test my Django application.

My application itself is configured to use a basic logger in settings.py using:

logging.basicConfig(level=logging.DEBUG)

And in my application code using:

logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))

However, when running unittests, I’d like to disable logging so that it doesn’t clutter my test result output. Is there a simple way to turn off logging in a global way, so that the application specific loggers aren’t writing stuff out to the console when I run tests?


回答 0

logging.disable(logging.CRITICAL)

将禁用所有级别不低于或等于的日志记录调用CRITICAL。可以通过以下方式重新启用日志记录

logging.disable(logging.NOTSET)
logging.disable(logging.CRITICAL)

will disable all logging calls with levels less severe than or equal to CRITICAL. Logging can be re-enabled with

logging.disable(logging.NOTSET)

回答 1

由于您使用的是Django,因此可以将以下几行添加到settings.py中:

import sys
import logging

if len(sys.argv) > 1 and sys.argv[1] == 'test':
    logging.disable(logging.CRITICAL)

这样,您不必setUp()在测试中的每行中都添加该行。

您也可以通过这种方式对测试需求进行一些方便的更改。

还有另一种“更精细”的方法可以为测试添加细节,这就是您自己的测试运行者。

只需创建一个这样的类:

import logging

from django.test.simple import DjangoTestSuiteRunner
from django.conf import settings

class MyOwnTestRunner(DjangoTestSuiteRunner):
    def run_tests(self, test_labels, extra_tests=None, **kwargs):

        # Don't show logging messages while testing
        logging.disable(logging.CRITICAL)

        return super(MyOwnTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)

现在添加到您的settings.py文件中:

TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')

这使您可以进行一种非常方便的修改,而另一种方法则不需要,这就是使Django仅测试所需的应用程序。您可以通过更改test_labels将以下行添加到测试运行器来实现:

if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]

Since you are in Django, you could add these lines to your settings.py:

import sys
import logging

if len(sys.argv) > 1 and sys.argv[1] == 'test':
    logging.disable(logging.CRITICAL)

That way you don’t have to add that line in every setUp() on your tests.

You could also do a couple of handy changes for your test needs this way.

There is another “nicer” or “cleaner” way to add specifics to your tests and that is making your own test runner.

Just create a class like this:

import logging

from django.test.simple import DjangoTestSuiteRunner
from django.conf import settings

class MyOwnTestRunner(DjangoTestSuiteRunner):
    def run_tests(self, test_labels, extra_tests=None, **kwargs):

        # Don't show logging messages while testing
        logging.disable(logging.CRITICAL)

        return super(MyOwnTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)

And now add to your settings.py file:

TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')

This lets you do one really handy modification that the other approach doesn’t, which is to make Django just tests the applications that you want. You can do that by changing the test_labels adding this line to the test runner:

if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]

回答 2

有没有一种简单的方法可以以全局方式关闭日志记录,以便在运行测试时,特定于应用程序的记录器不会将内容写到控制台上?

其他答案通过全局设置日志记录基础结构以忽略任何内容来防止“将内容写到控制台”。这行得通,但我觉得这种方法太钝了。我的方法是执行配置更改,该更改只执行防止日志从控制台中丢失所需的操作。所以我添加了一个自定义的日志过滤器到我的settings.py

from logging import Filter

class NotInTestingFilter(Filter):

    def filter(self, record):
        # Although I normally just put this class in the settings.py
        # file, I have my reasons to load settings here. In many
        # cases, you could skip the import and just read the setting
        # from the local symbol space.
        from django.conf import settings

        # TESTING_MODE is some settings variable that tells my code
        # whether the code is running in a testing environment or
        # not. Any test runner I use will load the Django code in a
        # way that makes it True.
        return not settings.TESTING_MODE

将Django日志配置为使用过滤器:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'testing': {
            '()': NotInTestingFilter
        }
    },
    'formatters': {
        'verbose': {
            'format': ('%(levelname)s %(asctime)s %(module)s '
                       '%(process)d %(thread)d %(message)s')
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'filters': ['testing'],
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'foo': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

最终结果:当我进行测试时,没有任何内容进入控制台,但其他一切保持不变。

为什么这样做?

我设计的代码包含仅在特定情况下触发的日志记录指令,如果出现问题,该指令应输出我诊断所需的确切数据。因此,我测试了他们执行了应做的事情,因此完全禁用日志记录对我而言不可行。我不想在软件投入生产后发现我认为要记录的内容没有记录下来。

此外,一些测试运行程序(例如,Nose)将在测试过程中捕获日志,并输出日志的相关部分以及测试失败。在弄清楚测试失败的原因时很有用。如果日志记录已完全关闭,则无法捕获任何内容。

Is there a simple way to turn off logging in a global way, so that the application specific loggers aren’t writing stuff out to the console when I run tests?

The other answers prevent “writing stuff out to the console” by globally setting the logging infrastructure to ignore anything. This works but I find it too blunt an approach. My approach is to perform a configuration change which does only what’s needed to prevent logs to get out on the console. So I add a custom logging filter to my settings.py:

from logging import Filter

class NotInTestingFilter(Filter):

    def filter(self, record):
        # Although I normally just put this class in the settings.py
        # file, I have my reasons to load settings here. In many
        # cases, you could skip the import and just read the setting
        # from the local symbol space.
        from django.conf import settings

        # TESTING_MODE is some settings variable that tells my code
        # whether the code is running in a testing environment or
        # not. Any test runner I use will load the Django code in a
        # way that makes it True.
        return not settings.TESTING_MODE

And I configure the Django logging to use the filter:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'testing': {
            '()': NotInTestingFilter
        }
    },
    'formatters': {
        'verbose': {
            'format': ('%(levelname)s %(asctime)s %(module)s '
                       '%(process)d %(thread)d %(message)s')
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'filters': ['testing'],
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'foo': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

End result: when I’m testing, nothing goes to the console, but everything else stays the same.

Why Do This?

I design code that contains logging instructions that are triggered only in specific circumstances and that should output the exact data I need for diagnosis if things go wrong. Therefore I test that they do what they are supposed to do and thus completely disabling logging is not viable for me. I don’t want to find once the software is in production that what I thought would be logged is not logged.

Moreover, some test runners (Nose, for instance) will capture logs during testing and output the relevant part of the log together with a test failure. It is useful in figuring out why a test failed. If logging is completely turned off, then there’s nothing that can be captured.


回答 3

我喜欢Hassek的自定义测试跑步者想法。应该注意的DjangoTestSuiteRunner是,它不再是Django 1.6+中的默认测试运行程序,而是由代替DiscoverRunner。对于默认行为,测试运行器应类似于:

import logging

from django.test.runner import DiscoverRunner

class NoLoggingTestRunner(DiscoverRunner):
    def run_tests(self, test_labels, extra_tests=None, **kwargs):

        # disable logging below CRITICAL while testing
        logging.disable(logging.CRITICAL)

        return super(NoLoggingTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)

I like Hassek’s custom test runner idea. It should be noted that DjangoTestSuiteRunner is no longer the default test runner in Django 1.6+, it has been replaced by the DiscoverRunner. For default behaviour, the test runner should be more like:

import logging

from django.test.runner import DiscoverRunner

class NoLoggingTestRunner(DiscoverRunner):
    def run_tests(self, test_labels, extra_tests=None, **kwargs):

        # disable logging below CRITICAL while testing
        logging.disable(logging.CRITICAL)

        return super(NoLoggingTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)

回答 4

我发现对于unittest框架内或类似框架内的测试,安全禁用单元测试中不必要的日志记录的最有效方法是在特定测试用例的setUp/ tearDown方法中启用/禁用。这使一个目标明确地应在哪里禁用日志。您也可以在要测试的类的记录器上明确地执行此操作。

import unittest
import logging

class TestMyUnitTest(unittest.TestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.NOTSET)

I’ve found that for tests within unittest or similar a framework, the most effective way to safely disable unwanted logging in unit tests is to enable/disable in the setUp/tearDown methods of a particular test case. This lets one target specifically where logs should be disabled. You could also do this explicitly on the logger of the class you’re testing.

import unittest
import logging

class TestMyUnitTest(unittest.TestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.NOTSET)

回答 5

我正在使用一个简单的方法装饰器来仅在特定的测试方法中禁用日志记录。

def disable_logging(f):

    def wrapper(*args):
        logging.disable(logging.CRITICAL)
        result = f(*args)
        logging.disable(logging.NOTSET)

        return result

    return wrapper

然后像下面的示例一样使用它:

class ScenarioTestCase(TestCase):

    @disable_logging
    test_scenario(self):
        pass

I am using a simple method decorator to disable logging only in a particular test method.

def disable_logging(f):

    def wrapper(*args):
        logging.disable(logging.CRITICAL)
        result = f(*args)
        logging.disable(logging.NOTSET)

        return result

    return wrapper

And then I use it as in the following example:

class ScenarioTestCase(TestCase):

    @disable_logging
    test_scenario(self):
        pass

回答 6

有一些漂亮而干净的方法可以挂起使用unittest.mock.patch方法登录测试。

foo.py

import logging


logger = logging.getLogger(__name__)

def bar():
    logger.error('There is some error output here!')
    return True

tests.py

from unittest import mock, TestCase
from foo import bar


class FooBarTestCase(TestCase):
    @mock.patch('foo.logger', mock.Mock())
    def test_bar(self):
        self.assertTrue(bar())

并且python3 -m unittest tests不会产生任何日志输出。

There is some pretty and clean method to suspend logging in tests with unittest.mock.patch method.

foo.py:

import logging


logger = logging.getLogger(__name__)

def bar():
    logger.error('There is some error output here!')
    return True

tests.py:

from unittest import mock, TestCase
from foo import bar


class FooBarTestCase(TestCase):
    @mock.patch('foo.logger', mock.Mock())
    def test_bar(self):
        self.assertTrue(bar())

And python3 -m unittest tests will produce no logging output.


回答 7

有时您需要日志,有时则不需要。我的代码中有settings.py

import sys

if '--no-logs' in sys.argv:
    print('> Disabling logging levels of CRITICAL and below.')
    sys.argv.remove('--no-logs')
    logging.disable(logging.CRITICAL)

因此,如果使用--no-logs选项运行测试,则只会获得critical日志:

$ python ./manage.py tests --no-logs
> Disabling logging levels of CRITICAL and below.

如果要在持续集成流程中加快测试速度,这将非常有帮助。

Sometimes you want the logs and sometimes not. I have this code in my settings.py

import sys

if '--no-logs' in sys.argv:
    print('> Disabling logging levels of CRITICAL and below.')
    sys.argv.remove('--no-logs')
    logging.disable(logging.CRITICAL)

So if you run your test with the --no-logs options you’ll get only the critical logs:

$ python ./manage.py tests --no-logs
> Disabling logging levels of CRITICAL and below.

It’s very helpful if you want speedup the tests on your continuous integration flow.


回答 8

如果您不希望它在setUp()和tearDown()中反复打开/关闭它以进行单元测试(看不到原因),则每个类只能执行一次:

    import unittest
    import logging

    class TestMyUnitTest(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            logging.disable(logging.CRITICAL)
        @classmethod
        def tearDownClass(cls):
            logging.disable(logging.NOTSET)

If you don’t want it repeatedly turn it on/off in setUp() and tearDown() for unittest (don’t see the reason for that), you could just do it once per class:

    import unittest
    import logging

    class TestMyUnitTest(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            logging.disable(logging.CRITICAL)
        @classmethod
        def tearDownClass(cls):
            logging.disable(logging.NOTSET)

回答 9

如果我想暂时取消某个特定的记录器,我编写了一个有用的小上下文管理器:

from contextlib import contextmanager
import logging

@contextmanager
def disable_logger(name):
    """Temporarily disable a specific logger."""
    logger = logging.getLogger(name)
    old_value = logger.disabled
    logger.disabled = True
    try:
        yield
    finally:
        logger.disabled = old_value

然后,您可以像这样使用它:

class MyTestCase(TestCase):
    def test_something(self):
        with disable_logger('<logger name>'):
            # code that causes the logger to fire

这样做的好处是,with完成后将重新启用记录器(或将其设置回其先前的状态)。

In cases where I wish to temporarily suppress a specific logger, I’ve written a little context manager that I’ve found useful:

from contextlib import contextmanager
import logging

@contextmanager
def disable_logger(name):
    """Temporarily disable a specific logger."""
    logger = logging.getLogger(name)
    old_value = logger.disabled
    logger.disabled = True
    try:
        yield
    finally:
        logger.disabled = old_value

You then use it like:

class MyTestCase(TestCase):
    def test_something(self):
        with disable_logger('<logger name>'):
            # code that causes the logger to fire

This has the advantage that the logger is re-enabled (or set back to its prior state) once the with completes.


回答 10

您可以将其放在单元测试__init__.py文件的顶级目录中。这将禁用单元测试套件中的全局日志记录。

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)

You can put this in the top level directory for unit tests __init__.py file. This will disable logging globally in the unit test suite.

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)

回答 11

就我而言,我有一个settings/test.py专门为测试目的而创建的设置文件 ,如下所示:

from .base import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'test_db'
    }
}

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.MD5PasswordHasher',
)

LOGGING = {}

我把一个环境变量DJANGO_SETTINGS_MODULE=settings.test/etc/environment

In my case I have a settings file settings/test.py created specifically for testing purposes, here’s what it looks like:

from .base import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'test_db'
    }
}

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.MD5PasswordHasher',
)

LOGGING = {}

I put an environment variable DJANGO_SETTINGS_MODULE=settings.test to /etc/environment.


回答 12

如果您有用于测试,开发和生产的不同的初始化模块,则可以禁用任何内容或将其重定向到初始化程序中。我有local.py,test.py和production.py,它们都从common.y继承

common.py进行包括以下代码段在内的所有主要配置:

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'django.server': {
        '()': 'django.utils.log.ServerFormatter',
        'format': '[%(server_time)s] %(message)s',
    },
    'verbose': {
        'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
    },
    'simple': {
        'format': '%(levelname)s %(message)s'
    },
},
'filters': {
    'require_debug_true': {
        '()': 'django.utils.log.RequireDebugTrue',
    },
},
'handlers': {
    'django.server': {
        'level': 'INFO',
        'class': 'logging.StreamHandler',
        'formatter': 'django.server',
    },
    'console': {
        'level': 'DEBUG',
        'class': 'logging.StreamHandler',
        'formatter': 'simple'
    },
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler'
    }
},
'loggers': {
    'django': {
        'handlers': ['console'],
        'level': 'INFO',
        'propagate': True,
    },
    'celery.tasks': {
        'handlers': ['console'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'django.server': {
        'handlers': ['django.server'],
        'level': 'INFO',
        'propagate': False,
    },
}

然后在test.py我有这个:

console_logger = Common.LOGGING.get('handlers').get('console')
console_logger['class'] = 'logging.FileHandler
console_logger['filename'] = './unitest.log

这用FileHandler代替了控制台处理程序,意味着仍然可以记录日志,但是我不必接触生产代码库。

If you have different initaliser modules for test, dev and production then you can disable anything or redirect it in the initialser. I have local.py, test.py and production.py that all inherit from common.y

common.py does all the main config including this snippet :

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'django.server': {
        '()': 'django.utils.log.ServerFormatter',
        'format': '[%(server_time)s] %(message)s',
    },
    'verbose': {
        'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
    },
    'simple': {
        'format': '%(levelname)s %(message)s'
    },
},
'filters': {
    'require_debug_true': {
        '()': 'django.utils.log.RequireDebugTrue',
    },
},
'handlers': {
    'django.server': {
        'level': 'INFO',
        'class': 'logging.StreamHandler',
        'formatter': 'django.server',
    },
    'console': {
        'level': 'DEBUG',
        'class': 'logging.StreamHandler',
        'formatter': 'simple'
    },
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler'
    }
},
'loggers': {
    'django': {
        'handlers': ['console'],
        'level': 'INFO',
        'propagate': True,
    },
    'celery.tasks': {
        'handlers': ['console'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'django.server': {
        'handlers': ['django.server'],
        'level': 'INFO',
        'propagate': False,
    },
}

Then in test.py I have this:

console_logger = Common.LOGGING.get('handlers').get('console')
console_logger['class'] = 'logging.FileHandler
console_logger['filename'] = './unitest.log

This replaces the console handler with a FileHandler and means still get logging but I do not have to touch the production code base.


回答 13

如果您使用的是pytest

由于pytest捕获日志消息并仅在失败的测试中显示它们,因此您通常不希望禁用任何日志记录。相反,请使用单独的settings.py文件进行测试(例如test_settings.py),然后添加到其中:

LOGGING_CONFIG = None

这告诉Django完全跳过配置日志记录。的LOGGING设置将被忽略,可以从设置中删除。

使用这种方法,对于通过的测试,您将不会获得任何日志记录,对于失败的测试,您将获得所有可用的日志记录。

测试将使用由设置的日志记录运行pytest。您可以根据自己的喜好来配置它pytest(例如tox.ini)。要包括调试级别日志消息,请使用log_level = DEBUG(或相应的命令行参数)。

If you’re using pytest:

Since pytest captures log messages and only displays them for failed tests, you typically don’t want to disable any logging. Instead, use a separate settings.py file for tests (e.g., test_settings.py), and add to it:

LOGGING_CONFIG = None

This tells Django to skip configuring the logging altogether. The LOGGING setting will be ignored and can be removed from the settings.

With this approach, you don’t get any logging for passed tests, and you get all available logging for failed tests.

The tests will run using the logging that was set up by pytest. It can be configured to your liking in the pytest settings (e.g., tox.ini). To include debug level log messages, use log_level = DEBUG (or the corresponding command line argument).


Django 1.7抛出django.core.exceptions.AppRegistryNotReady:模型尚未加载

问题:Django 1.7抛出django.core.exceptions.AppRegistryNotReady:模型尚未加载

这是我的Windows系统上的追溯。

Traceback (most recent call last):
  File "D:\AMD\workspace\steelrumors\manage.py", line 9, in <module>
    django.setup()
  File "D:\AMD\Django\django-django-4c85a0d\django\__init__.py", line 21, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\registry.py", line 108, in populate
    app_config.import_models(all_models)
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\config.py", line 197, in import_models
    self.models_module = import_module(models_module_name)
  File "C:\Python27\lib\importlib\__init__.py", line 37, in import_module
    __import__(name)
  File "C:\Python27\lib\site-packages\registration\models.py", line 15, in <module>
    User = get_user_model()
  File "D:\AMD\Django\django-django-4c85a0d\django\contrib\auth\__init__.py", line 135, in get_user_model
    return django_apps.get_model(settings.AUTH_USER_MODEL)
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\registry.py", line 199, in get_model
    self.check_models_ready()
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\registry.py", line 131, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

我的manage.py看起来像这样:

import os
import sys
import django

if __name__ == "__main__":

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "steelrumors.settings")
    django.setup()
    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

当我尝试在Django 1.7中使用注册应用程序时收到此错误

This is the traceback on my windows system.

Traceback (most recent call last):
  File "D:\AMD\workspace\steelrumors\manage.py", line 9, in <module>
    django.setup()
  File "D:\AMD\Django\django-django-4c85a0d\django\__init__.py", line 21, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\registry.py", line 108, in populate
    app_config.import_models(all_models)
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\config.py", line 197, in import_models
    self.models_module = import_module(models_module_name)
  File "C:\Python27\lib\importlib\__init__.py", line 37, in import_module
    __import__(name)
  File "C:\Python27\lib\site-packages\registration\models.py", line 15, in <module>
    User = get_user_model()
  File "D:\AMD\Django\django-django-4c85a0d\django\contrib\auth\__init__.py", line 135, in get_user_model
    return django_apps.get_model(settings.AUTH_USER_MODEL)
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\registry.py", line 199, in get_model
    self.check_models_ready()
  File "D:\AMD\Django\django-django-4c85a0d\django\apps\registry.py", line 131, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

And my manage.py looks like this:

import os
import sys
import django

if __name__ == "__main__":

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "steelrumors.settings")
    django.setup()
    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

I get this error when i am trying to use registration app in Django 1.7


回答 0

这就是为我们和这些人解决的问题:

我们的项目从Django 1.4开始,然后是1.5,然后是1.7。我们的wsgi.py看起来像这样:

import os

from django.core.handlers.wsgi import WSGIHandler

os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.settings'
application = WSGIHandler()

当我更新到1.7样式的WSGI处理程序时:

import os

from django.core.wsgi import get_wsgi_application

os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.settings'
application = get_wsgi_application()

现在一切正常。

This is what solved it for us and these folks:

Our project started with Django 1.4, we went to 1.5 and then to 1.7. Our wsgi.py looked like this:

import os

from django.core.handlers.wsgi import WSGIHandler

os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.settings'
application = WSGIHandler()

When I updated to the 1.7 style WSGI handler:

import os

from django.core.wsgi import get_wsgi_application

os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.settings'
application = get_wsgi_application()

Everything works now.


回答 1

运行这些命令解决了我的问题(此答案的积分):

import django
django.setup()

但是我不确定为什么需要这个。评论将不胜感激。

Running these commands solved my problem (credit to this answer):

import django
django.setup()

However I’m not sure why I need this. Comments would be appreciated.


回答 2

问题出在您的注册应用中。django-registration似乎get_user_module()models.py在模块级别调用的(当应用程序注册过程仍在加载模型时)。这将不再起作用:

try:
    from django.contrib.auth import get_user_model
    User = get_user_model()
except ImportError:
    from django.contrib.auth.models import User    

我将这个模型文件更改为仅get_user_model()在方法内部调用(而不是在模块级别),并且在FK中使用类似以下内容的方法:

user = ForeignKey(settings.AUTH_USER_MODEL)

顺便说一句,django.setup()您的manage.py文件中不需要调用,而是在中为您调用execute_from_command_line。(来源

The issue is in your registration app. It seems django-registration calls get_user_module() in models.py at a module level (when models are still being loaded by the application registration process). This will no longer work:

try:
    from django.contrib.auth import get_user_model
    User = get_user_model()
except ImportError:
    from django.contrib.auth.models import User    

I’d change this models file to only call get_user_model() inside methods (and not at module level) and in FKs use something like:

user = ForeignKey(settings.AUTH_USER_MODEL)

BTW, the call to django.setup() shouldn’t be required in your manage.py file, it’s called for you in execute_from_command_line. (source)


回答 3

刚遇到同样的问题。问题是因为django-registration与django 1.7用户模型不兼容。

一个简单的解决方法是在已安装的django-registration模块上更改以下代码行:

try:
    from django.contrib.auth import get_user_model
    User = get_user_model()
except ImportError:
    from django.contrib.auth.models import User  

至::

from django.conf import settings
try:
    from django.contrib.auth import get_user_model
    User = settings.AUTH_USER_MODEL
except ImportError:
    from django.contrib.auth.models import User 

我的在.venv/local/lib/python2.7/site-packages/registration/models.py(virtualenv)

Just encountered the same issue. The problem is because of django-registration incompatible with django 1.7 user model.

A simple fix is to change these lines of code, at your installed django-registration module::

try:
    from django.contrib.auth import get_user_model
    User = get_user_model()
except ImportError:
    from django.contrib.auth.models import User  

to::

from django.conf import settings
try:
    from django.contrib.auth import get_user_model
    User = settings.AUTH_USER_MODEL
except ImportError:
    from django.contrib.auth.models import User 

Mine is at .venv/local/lib/python2.7/site-packages/registration/models.py (virtualenv)


回答 4

这对我适用于Django 1.9。要执行的Python脚本位于Django项目的根目录中。

    import django 
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PROJECT_NAME.settings")
    django.setup()
    from APP_NAME.models import *

将PROJECT_NAME和APP_NAME设置为您的

This works for me for Django 1.9 . The Python script to execute was in the root of the Django project.

    import django 
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PROJECT_NAME.settings")
    django.setup()
    from APP_NAME.models import *

Set PROJECT_NAME and APP_NAME to yours


回答 5

另一个选择是您在INSTALLED_APPS中有一个重复的条目。这引发了我测试的两个不同应用程序的错误。显然,这不是Django所要检查的事情,但是谁又傻又足以将同一应用程序两次放入列表中。我,那是谁。

Another option is that you have a duplicate entry in INSTALLED_APPS. That threw this error for two different apps I tested. Apparently it’s not something Django checks for, but then who’s silly enough to put the same app in the list twice. Me, that’s who.


回答 6

在运行manage.py之前,您是否需要输入Python虚拟环境?

我自己遇到了这个错误,那就是问题所在。

Do you have a Python virtual environment that you need to enter before you run manage.py?

I ran into this error myself, and that was the problem.


回答 7

使用djangocms并添加插件(在我的情况下:djangocms-cascade)时遇到了这个问题。当然,我必须将插件添加到INSTALLED_APPS。但是顺序在这里很重要。

将“ cmsplugin_cascade”放置 “ cms” 之前即可解决此问题。

I ran into this issue when I use djangocms and added a plugin (in my case: djangocms-cascade). Of course I had to add the plugin to the INSTALLED_APPS. But the order is here important.

To place ‘cmsplugin_cascade’ before ‘cms’ solved the issue.


回答 8

如果使用django 1.7,请安装django-registration-redux == 1.1代替django-registration

install django-registration-redux==1.1 instead django-registration, if you using django 1.7


回答 9

./manage.py migrate

这解决了我的问题

./manage.py migrate

This solved my issue


回答 10

manage.py的“错误”;我不知道您从哪里得到它,但是那不是1.7- manage.py您使用的是一些时髦的预发布版本还是什么?

将您的manage.py设置重置为常规设置,如下所示,一切都会正常进行:

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

Your manage.py is “wrong”; I don’t know where you got it from, but that’s not a 1.7 manage.py – were you using some funky pre-release build or something?

Reset your manage.py to the conventional, as below, and things Should Just Work:

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

Django自引用外键

问题:Django自引用外键

一般来说,我对Web应用程序和数据库内容还是陌生的,所以这可能是一个愚蠢的问题。我想制作一个模型(“ CategoryModel”),其字段指向模型的另一个实例(其父实例)的主要ID。

class CategoryModel(models.Model):
    parent = models.ForeignKey(CategoryModel)

我该怎么做呢?谢谢!

I’m kind of new to webapps and database stuff in general so this might be a dumb question. I want to make a model (“CategoryModel”) with a field that points to the primary id of another instance of the model (its parent).

class CategoryModel(models.Model):
    parent = models.ForeignKey(CategoryModel)

How do I do this? Thanks!


回答 0

您可以将模型的名称作为字符串传递给ForeignKey,它将做正确的事情。

所以:

parent = models.ForeignKey("CategoryModel")

或者您可以使用字符串“ self”

parent = models.ForeignKey("self")

You can pass in the name of a model as a string to ForeignKey and it will do the right thing.

So:

parent = models.ForeignKey("CategoryModel")

Or you can use the string “self”

parent = models.ForeignKey("self")

回答 1

您可以使用字符串“ self”表示自我参考。

class CategoryModel(models.Model):
    parent = models.ForeignKey('self')

https://docs.djangoproject.com/zh-CN/dev/ref/models/fields/#foreignkey

You can use the string ‘self’ to indicate a self-reference.

class CategoryModel(models.Model):
    parent = models.ForeignKey('self')

https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey


回答 2

https://books.agiliq.com/projects/django-orm-cookbook/zh-CN/latest/self_fk.html

class Employee(models.Model):
    manager = models.ForeignKey('self', on_delete=models.CASCADE)

要么

class Employee(models.Model):
    manager = models.ForeignKey("app.Employee", on_delete=models.CASCADE)

https://stackabuse.com/recursive-model-relationships-in-django/

https://books.agiliq.com/projects/django-orm-cookbook/en/latest/self_fk.html

class Employee(models.Model):
    manager = models.ForeignKey('self', on_delete=models.CASCADE)

OR

class Employee(models.Model):
    manager = models.ForeignKey("app.Employee", on_delete=models.CASCADE)

https://stackabuse.com/recursive-model-relationships-in-django/


回答 3

您还要设置null = True和blank = True

class CategoryModel(models.Model):
    parent = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True)

null = True,允许在数据库中
blank = True,允许在表单中验证

You also to sett null=True and blank=True

class CategoryModel(models.Model):
    parent = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True)

null=True, to allow in database
blank=True, to allow in form validation


从URL检索参数

问题:从URL检索参数

给定如下所示的URL,如何解析查询参数的值?例如,在这种情况下,我需要的值def

/abc?def='ghi'

我在我的环境中使用Django;有没有一种方法request可以帮助我?

我尝试使用,self.request.get('def')但是它没有返回ghi我希望的值。

Given a URL like the following, how can I parse the value of the query parameters? For example, in this case I want the value of def.

/abc?def='ghi'

I am using Django in my environment; is there a method on the request object that could help me?

I tried using self.request.get('def') but it is not returning the value ghi as I had hoped.


回答 0

Python 2:

import urlparse
url = 'http://foo.appspot.com/abc?def=ghi'
parsed = urlparse.urlparse(url)
print urlparse.parse_qs(parsed.query)['def']

Python 3:

import urllib.parse as urlparse
from urllib.parse import parse_qs
url = 'http://foo.appspot.com/abc?def=ghi'
parsed = urlparse.urlparse(url)
print(parse_qs(parsed.query)['def'])

parse_qs返回值列表,因此上述代码将打印['ghi']

这是Python 3文档。

Python 2:

import urlparse
url = 'http://foo.appspot.com/abc?def=ghi'
parsed = urlparse.urlparse(url)
print urlparse.parse_qs(parsed.query)['def']

Python 3:

import urllib.parse as urlparse
from urllib.parse import parse_qs
url = 'http://foo.appspot.com/abc?def=ghi'
parsed = urlparse.urlparse(url)
print(parse_qs(parsed.query)['def'])

parse_qs returns a list of values, so the above code will print ['ghi'].

Here’s the Python 3 documentation.


回答 1

我很震惊这个解决方案还没有在这里。用:

request.GET.get('variable_name')

这将从“ GET”字典中“获取”变量,并返回“ variable_name”值(如果存在),或者返回None对象(如果不存在)。

I’m shocked this solution isn’t on here already. Use:

request.GET.get('variable_name')

This will “get” the variable from the “GET” dictionary, and return the ‘variable_name’ value if it exists, or a None object if it doesn’t exist.


回答 2

import urlparse
url = 'http://example.com/?q=abc&p=123'
par = urlparse.parse_qs(urlparse.urlparse(url).query)

print par['q'][0], par['p'][0]
import urlparse
url = 'http://example.com/?q=abc&p=123'
par = urlparse.parse_qs(urlparse.urlparse(url).query)

print par['q'][0], par['p'][0]

回答 3

对于Python> 3.4

from urllib import parse
url = 'http://foo.appspot.com/abc?def=ghi'
query_def=parse.parse_qs(parse.urlparse(url).query)['def'][0]

for Python > 3.4

from urllib import parse
url = 'http://foo.appspot.com/abc?def=ghi'
query_def=parse.parse_qs(parse.urlparse(url).query)['def'][0]

回答 4

有一个名为furl的新库。我发现这个库是执行url代数的最pythonic。安装:

pip install furl

码:

from furl import furl
f = furl("/abc?def='ghi'") 
print f.args['def']

There is a new library called furl. I find this library to be most pythonic for doing url algebra. To install:

pip install furl

Code:

from furl import furl
f = furl("/abc?def='ghi'") 
print f.args['def']

回答 5

我知道这有点晚了,但是自从我今天在这里找到自己以来,我认为这对其他人可能是一个有用的答案。

import urlparse
url = 'http://example.com/?q=abc&p=123'
parsed = urlparse.urlparse(url)
params = urlparse.parse_qsl(parsed.query)
for x,y in params:
    print "Parameter = "+x,"Value = "+y

使用parse_qsl(),“将数据作为名称,值对的列表返回。”

I know this is a bit late but since I found myself on here today, I thought that this might be a useful answer for others.

import urlparse
url = 'http://example.com/?q=abc&p=123'
parsed = urlparse.urlparse(url)
params = urlparse.parse_qsl(parsed.query)
for x,y in params:
    print "Parameter = "+x,"Value = "+y

With parse_qsl(), “Data are returned as a list of name, value pairs.”


回答 6

您引用的url是一种查询类型,我看到请求对象支持一种称为arguments的方法来获取查询参数。您可能还希望self.request.get('def')直接尝试从对象中获取价值。

The url you are referring is a query type and I see that the request object supports a method called arguments to get the query arguments. You may also want try self.request.get('def') directly to get your value from the object..


回答 7

def getParams(url):
    params = url.split("?")[1]
    params = params.split('=')
    pairs = zip(params[0::2], params[1::2])
    answer = dict((k,v) for k,v in pairs)

希望这可以帮助

def getParams(url):
    params = url.split("?")[1]
    params = params.split('=')
    pairs = zip(params[0::2], params[1::2])
    answer = dict((k,v) for k,v in pairs)

Hope this helps


回答 8

urlparse模块提供您所需的一切:

urlparse.parse_qs()

The urlparse module provides everything you need:

urlparse.parse_qs()


回答 9

不需要做任何事情。只有

self.request.get('variable_name')

注意,我没有指定方法(GET,POST等)。这是有据可查的这是一个示例

您使用Django模板的事实并不意味着处理程序也由Django处理

There’s not need to do any of that. Only with

self.request.get('variable_name')

Notice that I’m not specifying the method (GET, POST, etc). This is well documented and this is an example

The fact that you use Django templates doesn’t mean the handler is processed by Django as well


回答 10

在纯Python中:

def get_param_from_url(url, param_name):
    return [i.split("=")[-1] for i in url.split("?", 1)[-1].split("&") if i.startswith(param_name + "=")][0]

In pure Python:

def get_param_from_url(url, param_name):
    return [i.split("=")[-1] for i in url.split("?", 1)[-1].split("&") if i.startswith(param_name + "=")][0]

回答 11

import cgitb
cgitb.enable()

import cgi
print "Content-Type: text/plain;charset=utf-8"
print
form = cgi.FieldStorage()
i = int(form.getvalue('a'))+int(form.getvalue('b'))
print i
import cgitb
cgitb.enable()

import cgi
print "Content-Type: text/plain;charset=utf-8"
print
form = cgi.FieldStorage()
i = int(form.getvalue('a'))+int(form.getvalue('b'))
print i

回答 12

顺便说一句,我在使用parse_qs()并获取空值参数时遇到了问题,并了解到您必须传递第二个可选参数’keep_blank_values’才能在查询字符串中返回不包含任何值的参数列表。默认为false。某些糟糕的书面API要求参数存在,即使它们不包含任何值

for k,v in urlparse.parse_qs(p.query, True).items():
  print k

Btw, I was having issues using parse_qs() and getting empty value parameters and learned that you have to pass a second optional parameter ‘keep_blank_values’ to return a list of the parameters in a query string that contain no values. It defaults to false. Some crappy written APIs require parameters to be present even if they contain no values

for k,v in urlparse.parse_qs(p.query, True).items():
  print k

回答 13

有一个不错的库w3lib.url

from w3lib.url import url_query_parameter
url = "/abc?def=ghi"
print url_query_parameter(url, 'def')
ghi

There is a nice library w3lib.url

from w3lib.url import url_query_parameter
url = "/abc?def=ghi"
print url_query_parameter(url, 'def')
ghi

回答 14

参数= dict([part.split(’=’)代表get_parsed_url [4] .split(’&’)]的部分)

这很简单。可变参数将包含所有参数的字典。

parameters = dict([part.split(‘=’) for part in get_parsed_url[4].split(‘&’)])

This one is simple. The variable parameters will contain a dictionary of all the parameters.


回答 15

我看到龙卷风用户没有答案:

key = self.request.query_arguments.get("key", None)

此方法必须在派生自以下对象的处理程序中起作用:

tornado.web.RequestHandler

当找不到所请求的密钥时,此方法将返回的答案是无。这为您节省了一些异常处理。

I see there isn’t an answer for users of Tornado:

key = self.request.query_arguments.get("key", None)

This method must work inside an handler that is derived from:

tornado.web.RequestHandler

None is the answer this method will return when the requested key can’t be found. This saves you some exception handling.


如何在Django模板中显示当前年份?

问题:如何在Django模板中显示当前年份?

什么是内置模板标签以动态显示当前年份。像“ 2011”一样,显示该模板的模板标记是什么?

What is the inbuilt template tag to display the present year dynamically. Like “2011” what would be the template tag to display that?


回答 0

仅打印当前年份的完整标签是{% now "Y" %}。请注意,Y必须用引号引起来。

The full tag to print just the current year is {% now "Y" %}. Note that the Y must be in quotes.


回答 1

{% now 'Y' %} 是正确的语法

{% now 'Y' %} is the correct syntax


回答 2


回答 3

我在基于Django的网站http://pmtboyshostelraipur.pythonanywhere.com/中使用了以下命令,该工具可以正常运行(也许,当您阅读本文时,它已经停止工作,因为这是免费托管,所以试试看代码)。

{% now 'Y' %}

您可以使用以下代码在我显示了本年度的页脚部分中访问和查看(省略CSS部分,因此请使用您自己的)。

<footer class="container-fluid" id="footer">
    <center>
        <p>
           &copy;
           {% now 'Y' %}, 
           PMT Boys hostel <br> 
           All rights reserved
        </p>
    </center>
</footer>

它在我的网站的页脚中显示以下居中文本。

©2018, PMT Boys hostel 
All rights reserved

I have used the following in my Django based website http://pmtboyshostelraipur.pythonanywhere.com/ which works fine as expected (Maybe, by the time you’re reading this post, it would have stopped working because that’s a free hosting so just try in code and see).

{% now 'Y' %}

You can visit & see it in the footer part where I have displayed the current year using the below code(CSS part is omitted so use your own).

<footer class="container-fluid" id="footer">
    <center>
        <p>
           &copy;
           {% now 'Y' %}, 
           PMT Boys hostel <br> 
           All rights reserved
        </p>
    </center>
</footer>

And it is displaying the following centred text in my website’s footer.

©2018, PMT Boys hostel 
All rights reserved

回答 4

在我的模板中,除了当年之外,我还需要一个具有20个值(从当年开始)的信用卡到期年份下拉菜单。在select需要的值是2位和显示字符串4个位数。为了避免复杂的模板代码,我编写了这个简单的模板标签:

@register.filter
def add_current_year(int_value, digits=4):
    if digits == 2:
        return '%02d' % (int_value + datetime.datetime.now().year - 2000)
    return '%d' % (int_value + datetime.datetime.now().year)

并以以下方式使用它:

<select name="card_exp_year">
    {% for i in 'iiiiiiiiiiiiiiiiiiii' %}
    <option value="{{ forloop.counter0|add_current_year:2 }}">{{ forloop.counter0|add_current_year:4 }}</option>
    {% endfor %}
</select>

In my template, aside from the current year, I needed a credit card expiration year dropdown with 20 values (starting with the current year). The select values needed to be 2 digits and the display strings 4 digits. To avoid complex template code, I wrote this simple template tag:

@register.filter
def add_current_year(int_value, digits=4):
    if digits == 2:
        return '%02d' % (int_value + datetime.datetime.now().year - 2000)
    return '%d' % (int_value + datetime.datetime.now().year)

And used it in the following manner:

<select name="card_exp_year">
    {% for i in 'iiiiiiiiiiiiiiiiiiii' %}
    <option value="{{ forloop.counter0|add_current_year:2 }}">{{ forloop.counter0|add_current_year:4 }}</option>
    {% endfor %}
</select>

当您的应用具有测试目录时,在Django中运行特定的测试用例

问题:当您的应用具有测试目录时,在Django中运行特定的测试用例

Django文档(http://docs.djangoproject.com/en/1.3/topics/testing/#running-tests)指出,您可以通过指定单个测试用例来运行它们:

$ ./manage.py test animals.AnimalTestCase

假设您将测试保存在Django应用程序的tests.py文件中。如果是这样,那么此命令将按预期工作。

我在tests目录中有针对Django应用程序的测试:

my_project/apps/my_app/
├── __init__.py
├── tests
   ├── __init__.py
   ├── field_tests.py
   ├── storage_tests.py
├── urls.py
├── utils.py
└── views.py

tests/__init__.py文件具有suite()函数:

import unittest

from my_project.apps.my_app.tests import field_tests, storage_tests

def suite():
    tests_loader = unittest.TestLoader().loadTestsFromModule
    test_suites = []
    test_suites.append(tests_loader(field_tests))
    test_suites.append(tests_loader(storage_tests))
    return unittest.TestSuite(test_suites)

要运行测试,请执行以下操作:

$ ./manage.py test my_app

尝试指定单个测试用例会引发异常:

$ ./manage.py test my_app.tests.storage_tests.StorageTestCase
...
ValueError: Test label 'my_app.tests.storage_tests.StorageTestCase' should be of the form app.TestCase or app.TestCase.test_method

我试图做异常消息说:

$ ./manage.py test my_app.StorageTestCase
...
ValueError: Test label 'my_app.StorageTestCase' does not refer to a test

当我的测试位于多个文件中时,如何指定单个测试用例?

The Django documentation (http://docs.djangoproject.com/en/1.3/topics/testing/#running-tests) says that you can run individual test cases by specifying them:

$ ./manage.py test animals.AnimalTestCase

This assumes that you have your tests in a tests.py file in your Django application. If this is true, then this command works like expected.

I have my tests for a Django application in a tests directory:

my_project/apps/my_app/
├── __init__.py
├── tests
│   ├── __init__.py
│   ├── field_tests.py
│   ├── storage_tests.py
├── urls.py
├── utils.py
└── views.py

The tests/__init__.py file has a suite() function:

import unittest

from my_project.apps.my_app.tests import field_tests, storage_tests

def suite():
    tests_loader = unittest.TestLoader().loadTestsFromModule
    test_suites = []
    test_suites.append(tests_loader(field_tests))
    test_suites.append(tests_loader(storage_tests))
    return unittest.TestSuite(test_suites)

To run the tests I do:

$ ./manage.py test my_app

Trying to specify an individual test case raises an exception:

$ ./manage.py test my_app.tests.storage_tests.StorageTestCase
...
ValueError: Test label 'my_app.tests.storage_tests.StorageTestCase' should be of the form app.TestCase or app.TestCase.test_method

I tried to do what the exception message said:

$ ./manage.py test my_app.StorageTestCase
...
ValueError: Test label 'my_app.StorageTestCase' does not refer to a test

How do I specify an individual test case when my tests are in multiple files?


回答 0

结帐django-nose。它允许您指定测试运行方式:

python manage.py test another.test:TestCase.test_method

或如注释中所述,使用以下语法:

python manage.py test another.test.TestCase.test_method

Checkout django-nose. It allows you to specify tests to run like:

python manage.py test another.test:TestCase.test_method

or as noted in comments, use the syntax:

python manage.py test another.test.TestCase.test_method

回答 1

从Django 1.6开始,您可以对要运行的元素使用完整的点符号来运行完整的测试用例或单个测试。

现在,自动测试发现将在工作目录下以test开头的任何文件中找到测试,因此解决了您必须重命名文件的问题,但是现在您可以将其保留在所需的目录中。如果要使用自定义文件名,则可以使用option标志指定一个模式(默认Django测试运行器)--pattern="my_pattern_*.py"

因此,如果您在manage.py目录中,并且想要在app / module下的文件test_a内的TestCase子类中运行测试,则可以执行以下操作:Atests.pyexample

python manage.py test example.tests.A.test_a

如果您不想在Django 1.6或更高版本中包含依赖项,那么您将采用这种方式。

有关更多信息,请参见Django文档。

Since Django 1.6 you can run a complete test case, or single test, using the complete dot notation for the element you want to run.

Automatic test discovery will now find tests in any file that starts with test under the working directory, so addressing the question you would have to rename your files, but you can now keep them inside the directory you want. If you want to use custom file names you can specify a pattern (default Django test runner) with the option flag --pattern="my_pattern_*.py".

So if you are in your manage.py directory and want to run the test test_a inside TestCase subclass A inside a file tests.py under the app/module example you would do:

python manage.py test example.tests.A.test_a

If you don’t want to include a dependency and are in Django 1.6 or later that’s how you do it.

See the Django documentation for more information


回答 2

我自己遇到了这个问题,发现了这个问题,以防万一其他人出现,这就是我挖的东西。DjangoTestSuiteRuner使用一种称为build_test(label)的方法,该方法根据标签确定要运行哪些测试用例。研究此方法,结果发现他们正在“模型”或“测试”模块上执行getattr()。这意味着,如果您返回一个套件,则测试运行程序不在该套件中寻找您的测试用例,它只会查找那些模块中的一个。

一个快速的解决方法是__init__.py直接导入测试而不是定义套件。使它们成为“测试”模块的一部分,因此build_test(label)可以找到它们。

对于上面的示例,tests/__init__.py应仅包含:

from field_tests import *
from storage_tests import *

这不是很优雅,当然,如果您要对套件进行一些更复杂的操作,则此方法将无法正常工作,但在这种情况下可以。

I was having this problem myself and found this question, in case anyone else comes along, here was what I dug up. The DjangoTestSuiteRuner uses a method called build_test(label) that figures out what test cases to run based on the label. Looking into this method it turns out they’re doing a getattr() on either the “models” or “test” module. This means if you return a suite the test runner isn’t looking for your test cases in that suite, it only looks in one of those modules.

A quick work-around is to use __init__.py to import your tests directly instead of defining a suite. The makes them part of the “test” module and so build_test(label) can find them.

For your example above, tests/__init__.py should simply contain:

from field_tests import *
from storage_tests import *

This isn’t very elegant and of course if you’re trying to do something more complicated with your suite then this won’t work, but it would for this case.


回答 3

这应该工作-

python manage.py test my_app.tests.storage_tests

This should work-

python manage.py test my_app.tests.storage_tests

回答 4

我也遇到了这个问题,而不是使用django-nose,而是在此链接了:http : //www.pioverpi.net/2010/03/10/organizing-django-tests-into-folders/。您需要打开init .py并导入测试。

init .py中:from unique_test_file import *

I also ran into this problem and instead of using django-nose I followed this link here: http://www.pioverpi.net/2010/03/10/organizing-django-tests-into-folders/. You need to open you init.py and import your tests.

Ex in init.py: from unique_test_file import *


回答 5

将此代码放在__init__.py中,它将导入包和子包中的所有测试类。这将允许您运行特定的测试,而无需手动导入每个文件。

import pkgutil
import unittest

for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
    module = loader.find_module(module_name).load_module(module_name)
    for name in dir(module):
        obj = getattr(module, name)
        if isinstance(obj, type) and issubclass(obj, unittest.case.TestCase):
            exec ('%s = obj' % obj.__name__)

同样,对于您的测试套件,您可以简单地使用:

def suite():   
    return unittest.TestLoader().discover("appname.tests", pattern="*.py")

现在,您需要为新测试做的就是编写它们,并确保它们在tests文件夹中。不再需要繁琐的进口维修!

Put this code in your __init__.py and it will import all test classes in the package and subpackages. This will allow you to run specific tests without manually importing every file.

import pkgutil
import unittest

for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
    module = loader.find_module(module_name).load_module(module_name)
    for name in dir(module):
        obj = getattr(module, name)
        if isinstance(obj, type) and issubclass(obj, unittest.case.TestCase):
            exec ('%s = obj' % obj.__name__)

Similarly, for your test suite you can simply use:

def suite():   
    return unittest.TestLoader().discover("appname.tests", pattern="*.py")

Now all you have to do for new tests is write them and make sure they are in the tests folder. No more tedious maintenance of the imports!


传入的Django请求中的JSON数据在哪里?

问题:传入的Django请求中的JSON数据在哪里?

我正在尝试使用Django / Python处理传入的JSON / Ajax请求。

request.is_ajax()True在请求中,但是我不知道有效负载在哪里以及JSON数据。

request.POST.dir 包含以下内容:

['__class__', '__cmp__', '__contains__', '__copy__', '__deepcopy__', '__delattr__',
 '__delitem__', '__dict__', '__doc__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__setitem__', '__str__', '__weakref__', '_assert_mutable', '_encoding', 
'_get_encoding', '_mutable', '_set_encoding', 'appendlist', 'clear', 'copy', 'encoding', 
'fromkeys', 'get', 'getlist', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 
'keys', 'lists', 'pop', 'popitem', 'setdefault', 'setlist', 'setlistdefault', 'update', 
'urlencode', 'values']

在请求发布键中显然没有键。

当我查看Firebug中的POST时,请求中发送了JSON数据。

I’m trying to process incoming JSON/Ajax requests with Django/Python.

request.is_ajax() is True on the request, but I have no idea where the payload is with the JSON data.

request.POST.dir contains this:

['__class__', '__cmp__', '__contains__', '__copy__', '__deepcopy__', '__delattr__',
 '__delitem__', '__dict__', '__doc__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__setitem__', '__str__', '__weakref__', '_assert_mutable', '_encoding', 
'_get_encoding', '_mutable', '_set_encoding', 'appendlist', 'clear', 'copy', 'encoding', 
'fromkeys', 'get', 'getlist', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 
'keys', 'lists', 'pop', 'popitem', 'setdefault', 'setlist', 'setlistdefault', 'update', 
'urlencode', 'values']

There are apparently no keys in the request post keys.

When I look at the POST in Firebug, there is JSON data being sent up in the request.


回答 0

如果您要将JSON发布到Django,我想您想要request.bodyrequest.raw_post_data在Django <1.4上)。这将为您提供通过帖子发送的原始JSON数据。从那里您可以进一步处理它。

这是一个使用JavaScript,jQuery,jquery-json和Django 的示例。

JavaScript:

var myEvent = {id: calEvent.id, start: calEvent.start, end: calEvent.end,
               allDay: calEvent.allDay };
$.ajax({
    url: '/event/save-json/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: $.toJSON(myEvent),
    dataType: 'text',
    success: function(result) {
        alert(result.Result);
    }
});

Django:

def save_events_json(request):
    if request.is_ajax():
        if request.method == 'POST':
            print 'Raw Data: "%s"' % request.body   
    return HttpResponse("OK")

Django <1.4:

  def save_events_json(request):
    if request.is_ajax():
        if request.method == 'POST':
            print 'Raw Data: "%s"' % request.raw_post_data
    return HttpResponse("OK")

If you are posting JSON to Django, I think you want request.body (request.raw_post_data on Django < 1.4). This will give you the raw JSON data sent via the post. From there you can process it further.

Here is an example using JavaScript, jQuery, jquery-json and Django.

JavaScript:

var myEvent = {id: calEvent.id, start: calEvent.start, end: calEvent.end,
               allDay: calEvent.allDay };
$.ajax({
    url: '/event/save-json/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: $.toJSON(myEvent),
    dataType: 'text',
    success: function(result) {
        alert(result.Result);
    }
});

Django:

def save_events_json(request):
    if request.is_ajax():
        if request.method == 'POST':
            print 'Raw Data: "%s"' % request.body   
    return HttpResponse("OK")

Django < 1.4:

  def save_events_json(request):
    if request.is_ajax():
        if request.method == 'POST':
            print 'Raw Data: "%s"' % request.raw_post_data
    return HttpResponse("OK")

回答 1

我有同样的问题。我一直在发布复杂的JSON响应,但无法使用request.POST字典读取数据。

我的JSON POST数据为:

//JavaScript code:
//Requires json2.js and jQuery.
var response = {data:[{"a":1, "b":2},{"a":2, "b":2}]}
json_response = JSON.stringify(response); // proper serialization method, read 
                                          // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
$.post('url',json_response);

在这种情况下,您需要使用金黄色葡萄球菌提供的方法。阅读request.body并使用json stdlib反序列化。

#Django code:
import json
def save_data(request):
  if request.method == 'POST':
    json_data = json.loads(request.body) # request.raw_post_data w/ Django < 1.4
    try:
      data = json_data['data']
    except KeyError:
      HttpResponseServerError("Malformed data!")
    HttpResponse("Got json data")

I had the same problem. I had been posting a complex JSON response, and I couldn’t read my data using the request.POST dictionary.

My JSON POST data was:

//JavaScript code:
//Requires json2.js and jQuery.
var response = {data:[{"a":1, "b":2},{"a":2, "b":2}]}
json_response = JSON.stringify(response); // proper serialization method, read 
                                          // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
$.post('url',json_response);

In this case you need to use method provided by aurealus. Read the request.body and deserialize it with the json stdlib.

#Django code:
import json
def save_data(request):
  if request.method == 'POST':
    json_data = json.loads(request.body) # request.raw_post_data w/ Django < 1.4
    try:
      data = json_data['data']
    except KeyError:
      HttpResponseServerError("Malformed data!")
    HttpResponse("Got json data")

回答 2

方法一

客户:发送为 JSON

$.ajax({
    url: 'example.com/ajax/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    processData: false,
    data: JSON.stringify({'name':'John', 'age': 42}),
    ...
});

//Sent as a JSON object {'name':'John', 'age': 42}

服务器:

data = json.loads(request.body) # {'name':'John', 'age': 42}

方法二

客户端:发送为x-www-form-urlencoded
(注意:contentTypeprocessData已更改,JSON.stringify不需要)

$.ajax({
    url: 'example.com/ajax/',
    type: 'POST',    
    data: {'name':'John', 'age': 42},
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',  //Default
    processData: true,       
});

//Sent as a query string name=John&age=42

服务器:

data = request.POST # will be <QueryDict: {u'name':u'John', u'age': 42}>

在1.5+版本中进行了更改:https : //docs.djangoproject.com/en/dev/releases/1.5/#non-form-data-in-http-requests

HTTP请求中的非格式数据
request.POST将不再包含通过HTTP请求发布的数据,该数据头中包含非特定于格式的内容类型。在以前的版本中,以multipart / form-data或application / x-www-form-urlencoded以外的内容类型发布的数据仍将最终在request.POST属性中表示。对于这些情况,希望访问原始POST数据的开发人员应改用request.body属性。

可能相关

Method 1

Client : Send as JSON

$.ajax({
    url: 'example.com/ajax/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    processData: false,
    data: JSON.stringify({'name':'John', 'age': 42}),
    ...
});

//Sent as a JSON object {'name':'John', 'age': 42}

Server :

data = json.loads(request.body) # {'name':'John', 'age': 42}

Method 2

Client : Send as x-www-form-urlencoded
(Note: contentType & processData have changed, JSON.stringify is not needed)

$.ajax({
    url: 'example.com/ajax/',
    type: 'POST',    
    data: {'name':'John', 'age': 42},
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',  //Default
    processData: true,       
});

//Sent as a query string name=John&age=42

Server :

data = request.POST # will be <QueryDict: {u'name':u'John', u'age': 42}>

Changed in 1.5+ : https://docs.djangoproject.com/en/dev/releases/1.5/#non-form-data-in-http-requests

Non-form data in HTTP requests :
request.POST will no longer include data posted via HTTP requests with non form-specific content-types in the header. In prior versions, data posted with content-types other than multipart/form-data or application/x-www-form-urlencoded would still end up represented in the request.POST attribute. Developers wishing to access the raw POST data for these cases, should use the request.body attribute instead.

Probably related


回答 3

重要的是要记住,Python 3以不同的方式表示字符串-它们是字节数组。

使用Django 1.9和Python 2.7并在主体(而非标头)中发送JSON数据,您将使用类似以下内容:

mydata = json.loads(request.body)

但是对于Django 1.9和Python 3.4,您可以使用:

mydata = json.loads(request.body.decode("utf-8"))

我刚刚经历了这个学习过程,制作了我的第一个Py3 Django应用!

Its important to remember Python 3 has a different way to represent strings – they are byte arrays.

Using Django 1.9 and Python 2.7 and sending the JSON data in the main body (not a header) you would use something like:

mydata = json.loads(request.body)

But for Django 1.9 and Python 3.4 you would use:

mydata = json.loads(request.body.decode("utf-8"))

I just went through this learning curve making my first Py3 Django app!


回答 4

request.raw_response现在已弃用。request.body而是使用它来处理非常规表单数据,例如XML有效负载,二进制图像等。

有关此问题的Django文档

request.raw_response is now deprecated. Use request.body instead to process non-conventional form data such as XML payloads, binary images, etc.

Django documentation on the issue.


回答 5

在Django 1.6 python 3.3上

客户

$.ajax({
    url: '/urll/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify(json_object),
    dataType: 'json',
    success: function(result) {
        alert(result.Result);
    }
});

服务器

def urll(request):

if request.is_ajax():
    if request.method == 'POST':
        print ('Raw Data:', request.body) 

        print ('type(request.body):', type(request.body)) # this type is bytes

        print(json.loads(request.body.decode("utf-8")))

on django 1.6 python 3.3

client

$.ajax({
    url: '/urll/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify(json_object),
    dataType: 'json',
    success: function(result) {
        alert(result.Result);
    }
});

server

def urll(request):

if request.is_ajax():
    if request.method == 'POST':
        print ('Raw Data:', request.body) 

        print ('type(request.body):', type(request.body)) # this type is bytes

        print(json.loads(request.body.decode("utf-8")))

回答 6

HTTP POST有效负载只是一堆字节。Django(与大多数框架一样)通过URL编码参数或MIME多部分编码将其解码为字典。如果仅将JSON数据转储到POST内容中,则Django将不会对其进行解码。从完整的POST内容(而不是字典)中进行JSON解码;或将JSON数据放入MIME多部分包装器中。

简而言之,请显示JavaScript代码。问题似乎在那里。

The HTTP POST payload is just a flat bunch of bytes. Django (like most frameworks) decodes it into a dictionary from either URL encoded parameters, or MIME-multipart encoding. If you just dump the JSON data in the POST content, Django won’t decode it. Either do the JSON decoding from the full POST content (not the dictionary); or put the JSON data into a MIME-multipart wrapper.

In short, show the JavaScript code. The problem seems to be there.


回答 7

request.raw_post_data已不推荐使用。使用request.body替代

request.raw_post_data has been deprecated. Use request.body instead


回答 8

这样的事情。它的工作原理:从客户端请求数据

registerData = {
{% for field in userFields%}
  {{ field.name }}: {{ field.name }},
{% endfor %}
}


var request = $.ajax({
   url: "{% url 'MainApp:rq-create-account-json' %}",
   method: "POST",
   async: false,
   contentType: "application/json; charset=utf-8",
   data: JSON.stringify(registerData),
   dataType: "json"
});

request.done(function (msg) {
   [alert(msg);]
   alert(msg.name);
});

request.fail(function (jqXHR, status) {
  alert(status);
});

在服务器上处理请求

@csrf_exempt
def rq_create_account_json(request):
   if request.is_ajax():
       if request.method == 'POST':
           json_data = json.loads(request.body)
           print(json_data)
           return JsonResponse(json_data)
   return HttpResponse("Error")

Something like this. It’s worked: Request data from client

registerData = {
{% for field in userFields%}
  {{ field.name }}: {{ field.name }},
{% endfor %}
}


var request = $.ajax({
   url: "{% url 'MainApp:rq-create-account-json' %}",
   method: "POST",
   async: false,
   contentType: "application/json; charset=utf-8",
   data: JSON.stringify(registerData),
   dataType: "json"
});

request.done(function (msg) {
   [alert(msg);]
   alert(msg.name);
});

request.fail(function (jqXHR, status) {
  alert(status);
});

Process request at the server

@csrf_exempt
def rq_create_account_json(request):
   if request.is_ajax():
       if request.method == 'POST':
           json_data = json.loads(request.body)
           print(json_data)
           return JsonResponse(json_data)
   return HttpResponse("Error")

回答 9

html code 

file name  : view.html


    <!DOCTYPE html>
    <html>
    <head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
        $("#mySelect").change(function(){
            selected = $("#mySelect option:selected").text()
            $.ajax({
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json; charset=utf-8',
                url: '/view/',
                data: {
                       'fruit': selected
                      },
                success: function(result) {
                        document.write(result)
                        }
        });
      });
    });
    </script>
    </head>
    <body>

    <form>
        <br>
    Select your favorite fruit:
    <select id="mySelect">
      <option value="apple" selected >Select fruit</option>
      <option value="apple">Apple</option>
      <option value="orange">Orange</option>
      <option value="pineapple">Pineapple</option>
      <option value="banana">Banana</option>
    </select>
    </form>
    </body>
    </html>

Django code:


Inside views.py


def view(request):

    if request.method == 'POST':
        print request.body
        data = request.body
        return HttpResponse(json.dumps(data))
html code 

file name  : view.html


    <!DOCTYPE html>
    <html>
    <head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
        $("#mySelect").change(function(){
            selected = $("#mySelect option:selected").text()
            $.ajax({
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json; charset=utf-8',
                url: '/view/',
                data: {
                       'fruit': selected
                      },
                success: function(result) {
                        document.write(result)
                        }
        });
      });
    });
    </script>
    </head>
    <body>

    <form>
        <br>
    Select your favorite fruit:
    <select id="mySelect">
      <option value="apple" selected >Select fruit</option>
      <option value="apple">Apple</option>
      <option value="orange">Orange</option>
      <option value="pineapple">Pineapple</option>
      <option value="banana">Banana</option>
    </select>
    </form>
    </body>
    </html>

Django code:


Inside views.py


def view(request):

    if request.method == 'POST':
        print request.body
        data = request.body
        return HttpResponse(json.dumps(data))

回答 10

使用Angular您应该添加标头以请求或将其添加到模块配置标头: {'Content-Type': 'application/x-www-form-urlencoded'}

$http({
    url: url,
    method: method,
    timeout: timeout,
    data: data,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})

Using Angular you should add header to request or add it to module config headers: {'Content-Type': 'application/x-www-form-urlencoded'}

$http({
    url: url,
    method: method,
    timeout: timeout,
    data: data,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})

回答 11

request.POST只是一个类似于字典的对象,因此只需使用dict语法对其进行索引。

假设您的表单字段为fred,则可以执行以下操作:

if 'fred' in request.POST:
    mydata = request.POST['fred']

或者,使用表单对象处理POST数据。

request.POST is just a dictionary-like object, so just index into it with dict syntax.

Assuming your form field is fred, you could do something like this:

if 'fred' in request.POST:
    mydata = request.POST['fred']

Alternately, use a form object to deal with the POST data.


Django动态模型字段

问题:Django动态模型字段

我正在开发一个多租户应用程序,其中一些用户可以定义自己的数据字段(通过管理员)以收集表单中的其他数据并报告数据。后一点使得JSONField不是一个很好的选择,所以我有以下解决方案:

class CustomDataField(models.Model):
    """
    Abstract specification for arbitrary data fields.
    Not used for holding data itself, but metadata about the fields.
    """
    site = models.ForeignKey(Site, default=settings.SITE_ID)
    name = models.CharField(max_length=64)

    class Meta:
        abstract = True

class CustomDataValue(models.Model):
    """
    Abstract specification for arbitrary data.
    """
    value = models.CharField(max_length=1024)

    class Meta:
        abstract = True

请注意,CustomDataField如何具有指向站点的ForeignKey-每个站点将具有一组不同的自定义数据字段,但是使用相同的数据库。然后可以将各种具体的数据字段定义为:

class UserCustomDataField(CustomDataField):
    pass

class UserCustomDataValue(CustomDataValue):
    custom_field = models.ForeignKey(UserCustomDataField)
    user = models.ForeignKey(User, related_name='custom_data')

    class Meta:
        unique_together=(('user','custom_field'),)

这导致以下用途:

custom_field = UserCustomDataField.objects.create(name='zodiac', site=my_site) #probably created in the admin
user = User.objects.create(username='foo')
user_sign = UserCustomDataValue(custom_field=custom_field, user=user, data='Libra')
user.custom_data.add(user_sign) #actually, what does this even do?

但这感觉很笨拙,尤其是在需要手动创建相关数据并将其与具体模型关联的情况下。有没有更好的方法?

已被优先丢弃的选项:

  • 自定义SQL可以即时修改表。一方面是因为它无法扩展,另一方面是因为它太过分了。
  • 无模式的解决方案,例如NoSQL。我没有反对他们的想法,但他们仍然不合适。最终,这个数据类型化,并使用第三方报告应用的可能性是存在的。
  • 上面列出的JSONField,因为它不适用于查询。

I’m working on a multi-tenanted application in which some users can define their own data fields (via the admin) to collect additional data in forms and report on the data. The latter bit makes JSONField not a great option, so instead I have the following solution:

class CustomDataField(models.Model):
    """
    Abstract specification for arbitrary data fields.
    Not used for holding data itself, but metadata about the fields.
    """
    site = models.ForeignKey(Site, default=settings.SITE_ID)
    name = models.CharField(max_length=64)

    class Meta:
        abstract = True

class CustomDataValue(models.Model):
    """
    Abstract specification for arbitrary data.
    """
    value = models.CharField(max_length=1024)

    class Meta:
        abstract = True

Note how CustomDataField has a ForeignKey to Site – each Site will have a different set of custom data fields, but use the same database. Then the various concrete data fields can be defined as:

class UserCustomDataField(CustomDataField):
    pass

class UserCustomDataValue(CustomDataValue):
    custom_field = models.ForeignKey(UserCustomDataField)
    user = models.ForeignKey(User, related_name='custom_data')

    class Meta:
        unique_together=(('user','custom_field'),)

This leads to the following use:

custom_field = UserCustomDataField.objects.create(name='zodiac', site=my_site) #probably created in the admin
user = User.objects.create(username='foo')
user_sign = UserCustomDataValue(custom_field=custom_field, user=user, data='Libra')
user.custom_data.add(user_sign) #actually, what does this even do?

But this feels very clunky, particularly with the need to manually create the related data and associate it with the concrete model. Is there a better approach?

Options that have been pre-emptively discarded:

  • Custom SQL to modify tables on-the-fly. Partly because this won’t scale and partly because it’s too much of a hack.
  • Schema-less solutions like NoSQL. I have nothing against them, but they’re still not a good fit. Ultimately this data is typed, and the possibility exists of using a third-party reporting application.
  • JSONField, as listed above, as it’s not going to work well with queries.

回答 0

到目前为止,有四种可用的方法,其中两种需要特定的存储后端:

  1. Django-eav(不再提供原始软件包,但有一些繁荣的fork

    该解决方案基于实体属性值数据模型,实质上,它使用多个表来存储对象的动态属性。关于此解决方案的重要之处在于:

    • 使用几个纯净而简单的Django模型来表示动态字段,这使得它易于理解并且与数据库无关。
    • 允许您使用以下简单命令将动态属性存储有效地附加/分离到Django模型:

      eav.unregister(Encounter)
      eav.register(Patient)
    • 与Django admin很好地集成 ;

    • 同时真正强大。

    缺点:

    • 不太有效。这更多地是对EAV模式本身的一种批评,该模式要求将数据从列格式手动合并到模型中的一组键值对。
    • 难以维护。维护数据完整性需要多列唯一键约束,这在某些数据库上可能效率不高。
    • 您将需要选择其中一个分支,因为不再维护官方软件包,也没有明确的领导者。

    用法非常简单:

    import eav
    from app.models import Patient, Encounter
    
    eav.register(Encounter)
    eav.register(Patient)
    Attribute.objects.create(name='age', datatype=Attribute.TYPE_INT)
    Attribute.objects.create(name='height', datatype=Attribute.TYPE_FLOAT)
    Attribute.objects.create(name='weight', datatype=Attribute.TYPE_FLOAT)
    Attribute.objects.create(name='city', datatype=Attribute.TYPE_TEXT)
    Attribute.objects.create(name='country', datatype=Attribute.TYPE_TEXT)
    
    self.yes = EnumValue.objects.create(value='yes')
    self.no = EnumValue.objects.create(value='no')
    self.unkown = EnumValue.objects.create(value='unkown')
    ynu = EnumGroup.objects.create(name='Yes / No / Unknown')
    ynu.enums.add(self.yes)
    ynu.enums.add(self.no)
    ynu.enums.add(self.unkown)
    
    Attribute.objects.create(name='fever', datatype=Attribute.TYPE_ENUM,\
                                           enum_group=ynu)
    
    # When you register a model within EAV,
    # you can access all of EAV attributes:
    
    Patient.objects.create(name='Bob', eav__age=12,
                               eav__fever=no, eav__city='New York',
                               eav__country='USA')
    # You can filter queries based on their EAV fields:
    
    query1 = Patient.objects.filter(Q(eav__city__contains='Y'))
    query2 = Q(eav__city__contains='Y') |  Q(eav__fever=no)
  2. PostgreSQL中的Hstore,JSON或JSONB字段

    PostgreSQL支持几种更复杂的数据类型。大多数组件都通过第三方程序包得到支持,但是近年来Django已将它们引入django.contrib.postgres.fields中。

    HStoreField

    Django-hstore最初是第三方软件包,但是Django 1.8将HStoreField作为内置组件以及其他几种PostgreSQL支持的字段类型添加了。

    从某种意义上说,这种方法是好的,它可以让您充分利用两个领域:动态字段和关系数据库。但是,hstore 并不是理想的性能选择,特别是如果您最终要在一个字段中存储数千个项目时。它还仅支持值字符串。

    #app/models.py
    from django.contrib.postgres.fields import HStoreField
    class Something(models.Model):
        name = models.CharField(max_length=32)
        data = models.HStoreField(db_index=True)

    在Django的shell中,您可以像这样使用它:

    >>> instance = Something.objects.create(
                     name='something',
                     data={'a': '1', 'b': '2'}
               )
    >>> instance.data['a']
    '1'        
    >>> empty = Something.objects.create(name='empty')
    >>> empty.data
    {}
    >>> empty.data['a'] = '1'
    >>> empty.save()
    >>> Something.objects.get(name='something').data['a']
    '1'

    您可以针对hstore字段发出索引查询:

    # equivalence
    Something.objects.filter(data={'a': '1', 'b': '2'})
    
    # subset by key/value mapping
    Something.objects.filter(data__a='1')
    
    # subset by list of keys
    Something.objects.filter(data__has_keys=['a', 'b'])
    
    # subset by single key
    Something.objects.filter(data__has_key='a')    

    JSONField

    JSON / JSONB字段支持任何JSON可编码的数据类型,不仅是键/值对,而且比Hstore更快,并且(对于JSONB)更紧凑。一些软件包实现了JSON / JSONB字段,包括django-pgfields,但是从Django 1.9开始,JSONField是使用JSONB进行存储的内置方法。 JSONFieldHStoreField相似,并且在使用大字典时可能会表现更好。它还支持字符串以外的类型,例如整数,布尔值和嵌套字典。

    #app/models.py
    from django.contrib.postgres.fields import JSONField
    class Something(models.Model):
        name = models.CharField(max_length=32)
        data = JSONField(db_index=True)

    在外壳中创建:

    >>> instance = Something.objects.create(
                     name='something',
                     data={'a': 1, 'b': 2, 'nested': {'c':3}}
               )

    索引查询与HStoreField几乎相同,除了可以嵌套。复杂索引可能需要手动创建(或脚本迁移)。

    >>> Something.objects.filter(data__a=1)
    >>> Something.objects.filter(data__nested__c=3)
    >>> Something.objects.filter(data__has_key='a')
  3. Django MongoDB

    或其他NoSQL Django改编版-借助它们,您可以拥有完全动态的模型。

    NoSQL Django库很棒,但是请记住它们不是100%与Django兼容的,例如,要从标准Django 迁移到Django-nonrel,您将需要用ListField替换ManyToMany 。

    看看这个Django MongoDB示例:

    from djangotoolbox.fields import DictField
    
    class Image(models.Model):
        exif = DictField()
    ...
    
    >>> image = Image.objects.create(exif=get_exif_data(...))
    >>> image.exif
    {u'camera_model' : 'Spamcams 4242', 'exposure_time' : 0.3, ...}

    您甚至可以创建任何Django模型的嵌入式列表

    class Container(models.Model):
        stuff = ListField(EmbeddedModelField())
    
    class FooModel(models.Model):
        foo = models.IntegerField()
    
    class BarModel(models.Model):
        bar = models.CharField()
    ...
    
    >>> Container.objects.create(
        stuff=[FooModel(foo=42), BarModel(bar='spam')]
    )
  4. Django-mutant:基于syncdb和South-hooks的动态模型

    Django-mutant实现了完全动态的外键和m2m字段。灵感来自于Will Hardy和Michael Hall 令人难以置信但有些骇人听闻的解决方案。

    所有这些都基于Django South hooks,根据Will Hardy在DjangoCon 2011上的演讲 (观看!)仍然很健壮并已在生产中进行了测试(相关源代码)。

    首先实现这一点的迈克尔·霍尔

    是的,这是神奇的事情,通过这些方法,您可以使用任何关系数据库后端来实现完全动态的Django应用程序,模型和字段。但是要花多少钱呢?大量使用会损害应用的稳定性吗?这些是要考虑的问题。您需要确保保持适当的锁定,以允许同时进行数据库更改请求。

    如果使用的是Michael Halls lib,则代码将如下所示:

    from dynamo import models
    
    test_app, created = models.DynamicApp.objects.get_or_create(
                          name='dynamo'
                        )
    test, created = models.DynamicModel.objects.get_or_create(
                      name='Test',
                      verbose_name='Test Model',
                      app=test_app
                   )
    foo, created = models.DynamicModelField.objects.get_or_create(
                      name = 'foo',
                      verbose_name = 'Foo Field',
                      model = test,
                      field_type = 'dynamiccharfield',
                      null = True,
                      blank = True,
                      unique = False,
                      help_text = 'Test field for Foo',
                   )
    bar, created = models.DynamicModelField.objects.get_or_create(
                      name = 'bar',
                      verbose_name = 'Bar Field',
                      model = test,
                      field_type = 'dynamicintegerfield',
                      null = True,
                      blank = True,
                      unique = False,
                      help_text = 'Test field for Bar',
                   )

As of today, there are four available approaches, two of them requiring a certain storage backend:

  1. Django-eav (the original package is no longer mantained but has some thriving forks)

    This solution is based on Entity Attribute Value data model, essentially, it uses several tables to store dynamic attributes of objects. Great parts about this solution is that it:

    • uses several pure and simple Django models to represent dynamic fields, which makes it simple to understand and database-agnostic;
    • allows you to effectively attach/detach dynamic attribute storage to Django model with simple commands like:

      eav.unregister(Encounter)
      eav.register(Patient)
      
    • Nicely integrates with Django admin;

    • At the same time being really powerful.

    Downsides:

    • Not very efficient. This is more of a criticism of the EAV pattern itself, which requires manually merging the data from a column format to a set of key-value pairs in the model.
    • Harder to maintain. Maintaining data integrity requires a multi-column unique key constraint, which may be inefficient on some databases.
    • You will need to select one of the forks, since the official package is no longer maintained and there is no clear leader.

    The usage is pretty straightforward:

    import eav
    from app.models import Patient, Encounter
    
    eav.register(Encounter)
    eav.register(Patient)
    Attribute.objects.create(name='age', datatype=Attribute.TYPE_INT)
    Attribute.objects.create(name='height', datatype=Attribute.TYPE_FLOAT)
    Attribute.objects.create(name='weight', datatype=Attribute.TYPE_FLOAT)
    Attribute.objects.create(name='city', datatype=Attribute.TYPE_TEXT)
    Attribute.objects.create(name='country', datatype=Attribute.TYPE_TEXT)
    
    self.yes = EnumValue.objects.create(value='yes')
    self.no = EnumValue.objects.create(value='no')
    self.unkown = EnumValue.objects.create(value='unkown')
    ynu = EnumGroup.objects.create(name='Yes / No / Unknown')
    ynu.enums.add(self.yes)
    ynu.enums.add(self.no)
    ynu.enums.add(self.unkown)
    
    Attribute.objects.create(name='fever', datatype=Attribute.TYPE_ENUM,\
                                           enum_group=ynu)
    
    # When you register a model within EAV,
    # you can access all of EAV attributes:
    
    Patient.objects.create(name='Bob', eav__age=12,
                               eav__fever=no, eav__city='New York',
                               eav__country='USA')
    # You can filter queries based on their EAV fields:
    
    query1 = Patient.objects.filter(Q(eav__city__contains='Y'))
    query2 = Q(eav__city__contains='Y') |  Q(eav__fever=no)
    
  2. Hstore, JSON or JSONB fields in PostgreSQL

    PostgreSQL supports several more complex data types. Most are supported via third-party packages, but in recent years Django has adopted them into django.contrib.postgres.fields.

    HStoreField:

    Django-hstore was originally a third-party package, but Django 1.8 added HStoreField as a built-in, along with several other PostgreSQL-supported field types.

    This approach is good in a sense that it lets you have the best of both worlds: dynamic fields and relational database. However, hstore is not ideal performance-wise, especially if you are going to end up storing thousands of items in one field. It also only supports strings for values.

    #app/models.py
    from django.contrib.postgres.fields import HStoreField
    class Something(models.Model):
        name = models.CharField(max_length=32)
        data = models.HStoreField(db_index=True)
    

    In Django’s shell you can use it like this:

    >>> instance = Something.objects.create(
                     name='something',
                     data={'a': '1', 'b': '2'}
               )
    >>> instance.data['a']
    '1'        
    >>> empty = Something.objects.create(name='empty')
    >>> empty.data
    {}
    >>> empty.data['a'] = '1'
    >>> empty.save()
    >>> Something.objects.get(name='something').data['a']
    '1'
    

    You can issue indexed queries against hstore fields:

    # equivalence
    Something.objects.filter(data={'a': '1', 'b': '2'})
    
    # subset by key/value mapping
    Something.objects.filter(data__a='1')
    
    # subset by list of keys
    Something.objects.filter(data__has_keys=['a', 'b'])
    
    # subset by single key
    Something.objects.filter(data__has_key='a')    
    

    JSONField:

    JSON/JSONB fields support any JSON-encodable data type, not just key/value pairs, but also tend to be faster and (for JSONB) more compact than Hstore. Several packages implement JSON/JSONB fields including django-pgfields, but as of Django 1.9, JSONField is a built-in using JSONB for storage. JSONField is similar to HStoreField, and may perform better with large dictionaries. It also supports types other than strings, such as integers, booleans and nested dictionaries.

    #app/models.py
    from django.contrib.postgres.fields import JSONField
    class Something(models.Model):
        name = models.CharField(max_length=32)
        data = JSONField(db_index=True)
    

    Creating in the shell:

    >>> instance = Something.objects.create(
                     name='something',
                     data={'a': 1, 'b': 2, 'nested': {'c':3}}
               )
    

    Indexed queries are nearly identical to HStoreField, except nesting is possible. Complex indexes may require manually creation (or a scripted migration).

    >>> Something.objects.filter(data__a=1)
    >>> Something.objects.filter(data__nested__c=3)
    >>> Something.objects.filter(data__has_key='a')
    
  3. Django MongoDB

    Or other NoSQL Django adaptations — with them you can have fully dynamic models.

    NoSQL Django libraries are great, but keep in mind that they are not 100% the Django-compatible, for example, to migrate to Django-nonrel from standard Django you will need to replace ManyToMany with ListField among other things.

    Checkout this Django MongoDB example:

    from djangotoolbox.fields import DictField
    
    class Image(models.Model):
        exif = DictField()
    ...
    
    >>> image = Image.objects.create(exif=get_exif_data(...))
    >>> image.exif
    {u'camera_model' : 'Spamcams 4242', 'exposure_time' : 0.3, ...}
    

    You can even create embedded lists of any Django models:

    class Container(models.Model):
        stuff = ListField(EmbeddedModelField())
    
    class FooModel(models.Model):
        foo = models.IntegerField()
    
    class BarModel(models.Model):
        bar = models.CharField()
    ...
    
    >>> Container.objects.create(
        stuff=[FooModel(foo=42), BarModel(bar='spam')]
    )
    
  4. Django-mutant: Dynamic models based on syncdb and South-hooks

    Django-mutant implements fully dynamic Foreign Key and m2m fields. And is inspired by incredible but somewhat hackish solutions by Will Hardy and Michael Hall.

    All of these are based on Django South hooks, which, according to Will Hardy’s talk at DjangoCon 2011 (watch it!) are nevertheless robust and tested in production (relevant source code).

    First to implement this was Michael Hall.

    Yes, this is magic, with these approaches you can achieve fully dynamic Django apps, models and fields with any relational database backend. But at what cost? Will stability of application suffer upon heavy use? These are the questions to be considered. You need to be sure to maintain a proper lock in order to allow simultaneous database altering requests.

    If you are using Michael Halls lib, your code will look like this:

    from dynamo import models
    
    test_app, created = models.DynamicApp.objects.get_or_create(
                          name='dynamo'
                        )
    test, created = models.DynamicModel.objects.get_or_create(
                      name='Test',
                      verbose_name='Test Model',
                      app=test_app
                   )
    foo, created = models.DynamicModelField.objects.get_or_create(
                      name = 'foo',
                      verbose_name = 'Foo Field',
                      model = test,
                      field_type = 'dynamiccharfield',
                      null = True,
                      blank = True,
                      unique = False,
                      help_text = 'Test field for Foo',
                   )
    bar, created = models.DynamicModelField.objects.get_or_create(
                      name = 'bar',
                      verbose_name = 'Bar Field',
                      model = test,
                      field_type = 'dynamicintegerfield',
                      null = True,
                      blank = True,
                      unique = False,
                      help_text = 'Test field for Bar',
                   )
    

回答 1

我一直在努力推动django-dynamo的构想。该项目仍未记录在案,但您可以在https://github.com/charettes/django-mutant中阅读代码

实际上FK和M2M字段(请参阅contrib.related)也可以工作,甚至可以为自己的自定义字段定义包装器。

还支持模型选项,例如unique_together和ordering以及Model基类,因此您可以将模型代理,抽象或混合作为子类。

我实际上正在研究一种非内存锁定机制,以确保可以在多个django运行实例之间共享模型定义,同时防止使用过时的定义。

该项目仍处于Alpha状态,但这是我的一个项目的基础技术,因此我必须将其投入生产准备。大型计划还支持django-nonrel,因此我们可以利用mongodb驱动程序。

I’ve been working on pushing the django-dynamo idea further. The project is still undocumented but you can read the code at https://github.com/charettes/django-mutant.

Actually FK and M2M fields (see contrib.related) also work and it’s even possible to define wrapper for your own custom fields.

There’s also support for model options such as unique_together and ordering plus Model bases so you can subclass model proxy, abstract or mixins.

I’m actually working on a not in-memory lock mechanism to make sure model definitions can be shared accross multiple django running instances while preventing them using obsolete definition.

The project is still very alpha but it’s a cornerstone technology for one of my project so I’ll have to take it to production ready. The big plan is supporting django-nonrel also so we can leverage the mongodb driver.


回答 2

进一步的研究表明,这是实体属性值设计模式的一种特殊情况,该模式已通过几个软件包为Django实现。

首先,在PyPi上有一个原始的eav-django项目。

其次,第一个项目的最新分支是django-eav,它主要是一个重构,允许将EAV与django自己的模型或第三方应用程序中的模型一起使用。

Further research reveals that this is a somewhat special case of Entity Attribute Value design pattern, which has been implemented for Django by a couple of packages.

First, there’s the original eav-django project, which is on PyPi.

Second, there’s a more recent fork of the first project, django-eav which is primarily a refactor to allow use of EAV with django’s own models or models in third-party apps.


Django可选的url参数

问题:Django可选的url参数

我有一个像这样的Django URL:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

views.py:

def ProjectConfig(request, product, project_id=None, template_name='project.html'):
    ...
    # do stuff

问题是我希望project_id参数是可选的。

我希望/project_config/并且/project_config/12345abdce/成为同等有效的URL模式,以便如果 project_id通过,那么我可以使用它。

就目前而言,访问不带project_id参数的URL时会得到404 。

I have a Django URL like this:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

views.py:

def ProjectConfig(request, product, project_id=None, template_name='project.html'):
    ...
    # do stuff

The problem is that I want the project_id parameter to be optional.

I want /project_config/ and /project_config/12345abdce/ to be equally valid URL patterns, so that if project_id is passed, then I can use it.

As it stands at the moment, I get a 404 when I access the URL without the project_id parameter.


回答 0

有几种方法。

一种是在正则表达式中使用非捕获组:使正则 (?:/(?P<title>[a-zA-Z]+)/)?
表达式Django URL令牌为可选

另一种更容易遵循的方法是拥有多个符合您需求的规则,所有规则都指向同一视图。

urlpatterns = patterns('',
    url(r'^project_config/$', views.foo),
    url(r'^project_config/(?P<product>\w+)/$', views.foo),
    url(r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$', views.foo),
)

请记住,在您看来,您还需要为可选的URL参数设置默认值,否则会出现错误:

def foo(request, optional_parameter=''):
    # Your code goes here

There are several approaches.

One is to use a non-capturing group in the regex: (?:/(?P<title>[a-zA-Z]+)/)?
Making a Regex Django URL Token Optional

Another, easier to follow way is to have multiple rules that matches your needs, all pointing to the same view.

urlpatterns = patterns('',
    url(r'^project_config/$', views.foo),
    url(r'^project_config/(?P<product>\w+)/$', views.foo),
    url(r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$', views.foo),
)

Keep in mind that in your view you’ll also need to set a default for the optional URL parameter, or you’ll get an error:

def foo(request, optional_parameter=''):
    # Your code goes here

回答 1

您可以使用嵌套路线

Django <1.8

urlpatterns = patterns(''
    url(r'^project_config/', include(patterns('',
        url(r'^$', ProjectConfigView.as_view(), name="project_config")
        url(r'^(?P<product>\w+)$', include(patterns('',
            url(r'^$', ProductView.as_view(), name="product"),
            url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
        ))),
    ))),
)

Django> = 1.8

urlpatterns = [
    url(r'^project_config/', include([
        url(r'^$', ProjectConfigView.as_view(), name="project_config")
        url(r'^(?P<product>\w+)$', include([
            url(r'^$', ProductView.as_view(), name="product"),
            url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
        ])),
    ])),
]

这比DRY要多得多(假设您要将productkwarg 重命名为product_id,只需更改第4行,它就会影响以下网址。

针对Django 1.8及更高版本进行了编辑

You can use nested routes

Django <1.8

urlpatterns = patterns(''
    url(r'^project_config/', include(patterns('',
        url(r'^$', ProjectConfigView.as_view(), name="project_config")
        url(r'^(?P<product>\w+)$', include(patterns('',
            url(r'^$', ProductView.as_view(), name="product"),
            url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
        ))),
    ))),
)

Django >=1.8

urlpatterns = [
    url(r'^project_config/', include([
        url(r'^$', ProjectConfigView.as_view(), name="project_config")
        url(r'^(?P<product>\w+)$', include([
            url(r'^$', ProductView.as_view(), name="product"),
            url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
        ])),
    ])),
]

This is a lot more DRY (Say you wanted to rename the product kwarg to product_id, you only have to change line 4, and it will affect the below URLs.

Edited for Django 1.8 and above


回答 2

更简单的是使用:

(?P<project_id>\w+|)

“(a | b)”表示a或b,因此在您的情况下将是一个或多个文字字符(\ w +)或什么都没有。

因此,它看起来像:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

Even simpler is to use:

(?P<project_id>\w+|)

The “(a|b)” means a or b, so in your case it would be one or more word characters (\w+) or nothing.

So it would look like:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

回答 3

Django> 2.0版本

该方法与Yuji’Tomita’Tomita’s Answer中给出的方法基本相同。但是,受影响的语法是:

# URLconf
...

urlpatterns = [
    path(
        'project_config/<product>/',
        views.get_product, 
        name='project_config'
    ),
    path(
        'project_config/<product>/<project_id>/',
        views.get_product,
        name='project_config'
    ),
]


# View (in views.py)
def get_product(request, product, project_id='None'):
    # Output the appropriate product
    ...

使用,path()您还可以使用类型为的可选参数将额外的参数传递给视图。在这种情况下,您的视图不需要该属性的默认值:kwargsdictproject_id

    ...
    path(
        'project_config/<product>/',
        views.get_product,
        kwargs={'project_id': None},
        name='project_config'
    ),
    ...

有关如何在最新的Django版本中完成此操作的信息,请参阅有关URL调度的官方文档

Django > 2.0 version:

The approach is essentially identical with the one given in Yuji ‘Tomita’ Tomita’s Answer. Affected, however, is the syntax:

# URLconf
...

urlpatterns = [
    path(
        'project_config/<product>/',
        views.get_product, 
        name='project_config'
    ),
    path(
        'project_config/<product>/<project_id>/',
        views.get_product,
        name='project_config'
    ),
]


# View (in views.py)
def get_product(request, product, project_id='None'):
    # Output the appropriate product
    ...

Using path() you can also pass extra arguments to a view with the optional argument kwargs that is of type dict. In this case your view would not need a default for the attribute project_id:

    ...
    path(
        'project_config/<product>/',
        views.get_product,
        kwargs={'project_id': None},
        name='project_config'
    ),
    ...

For how this is done in the most recent Django version, see the official docs about URL dispatching.


回答 4

以为我会在答案中加点。

如果您有多个URL定义,则必须分别命名每个。因此,当调用反向时,您会失去灵活性,因为一个反向将需要一个参数,而另一个则不会。

使用正则表达式来容纳可选参数的另一种方法:

r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'

Thought I’d add a bit to the answer.

If you have multiple URL definitions then you’ll have to name each of them separately. So you lose the flexibility when calling reverse since one reverse will expect a parameter while the other won’t.

Another way to use regex to accommodate the optional parameter:

r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'

回答 5

的Django = 2.2

urlpatterns = [
    re_path(r'^project_config/(?:(?P<product>\w+)/(?:(?P<project_id>\w+)/)/)?$', tool.views.ProjectConfig, name='project_config')
]

Django = 2.2

urlpatterns = [
    re_path(r'^project_config/(?:(?P<product>\w+)/(?:(?P<project_id>\w+)/)/)?$', tool.views.ProjectConfig, name='project_config')
]

回答 6

用 ?工作正常,您可以检查pythex。请记住在视图方法的定义中添加参数* args和** kwargs

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')

Use ? work well, you can check on pythex. Remember to add the parameters *args and **kwargs in the definition of the view methods

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')

您如何捕获此异常?

问题:您如何捕获此异常?

这段代码在django / db / models / fields.py中。它创建/定义一个异常吗?

class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescriptorMethods)):
    # This class provides the functionality that makes the related-object
    # managers available as attributes on a model class, for fields that have
    # a single "remote" value, on the class that defines the related field.
    # In the example "choice.poll", the poll attribute is a
    # ReverseSingleRelatedObjectDescriptor instance.
    def __init__(self, field_with_rel):
        self.field = field_with_rel
        self.cache_name = self.field.get_cache_name()

    @cached_property
    def RelatedObjectDoesNotExist(self):
        # The exception can't be created at initialization time since the
        # related model might not be resolved yet; `rel.to` might still be
        # a string model reference.
        return type(
            str('RelatedObjectDoesNotExist'),
            (self.field.rel.to.DoesNotExist, AttributeError),
            {}
        )

这在django / db / models / fields / related.py中,它在上面引发了上述异常:

def __get__(self, instance, instance_type=None):
    if instance is None:
        return self
    try:
        rel_obj = getattr(instance, self.cache_name)
    except AttributeError:
        val = self.field.get_local_related_value(instance)
        if None in val:
            rel_obj = None
        else:
            params = dict(
                (rh_field.attname, getattr(instance, lh_field.attname))
                for lh_field, rh_field in self.field.related_fields)
            qs = self.get_queryset(instance=instance)
            extra_filter = self.field.get_extra_descriptor_filter(instance)
            if isinstance(extra_filter, dict):
                params.update(extra_filter)
                qs = qs.filter(**params)
            else:
                qs = qs.filter(extra_filter, **params)
            # Assuming the database enforces foreign keys, this won't fail.
            rel_obj = qs.get()
            if not self.field.rel.multiple:
                setattr(rel_obj, self.field.related.get_cache_name(), instance)
        setattr(instance, self.cache_name, rel_obj)
    if rel_obj is None and not self.field.null:
        raise self.RelatedObjectDoesNotExist(
            "%s has no %s." % (self.field.model.__name__, self.field.name)
        )
    else:
        return rel_obj

问题是此代码:

    try:
        val = getattr(obj, attr_name)
    except related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist:
        val = None  # Does not catch the thrown exception
    except Exception as foo:
        print type(foo)  # Catches here, not above

不会捕获该异常

>>>print type(foo)
<class 'django.db.models.fields.related.RelatedObjectDoesNotExist'>
>>>isinstance(foo, related.FieldDoesNotExist)
False

except related.RelatedObjectDoesNotExist:

提出一个 AttributeError: 'module' object has no attribute 'RelatedObjectDoesNotExist'

>>>isinstance(foo, related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist)
Traceback (most recent call last):
  File "<string>", line 1, in <fragment>
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

这可能是为什么。

This code is in django/db/models/fields.py It creates/defines an exception?

class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescriptorMethods)):
    # This class provides the functionality that makes the related-object
    # managers available as attributes on a model class, for fields that have
    # a single "remote" value, on the class that defines the related field.
    # In the example "choice.poll", the poll attribute is a
    # ReverseSingleRelatedObjectDescriptor instance.
    def __init__(self, field_with_rel):
        self.field = field_with_rel
        self.cache_name = self.field.get_cache_name()

    @cached_property
    def RelatedObjectDoesNotExist(self):
        # The exception can't be created at initialization time since the
        # related model might not be resolved yet; `rel.to` might still be
        # a string model reference.
        return type(
            str('RelatedObjectDoesNotExist'),
            (self.field.rel.to.DoesNotExist, AttributeError),
            {}
        )

This is in django/db/models/fields/related.py it raises the said exception above:

def __get__(self, instance, instance_type=None):
    if instance is None:
        return self
    try:
        rel_obj = getattr(instance, self.cache_name)
    except AttributeError:
        val = self.field.get_local_related_value(instance)
        if None in val:
            rel_obj = None
        else:
            params = dict(
                (rh_field.attname, getattr(instance, lh_field.attname))
                for lh_field, rh_field in self.field.related_fields)
            qs = self.get_queryset(instance=instance)
            extra_filter = self.field.get_extra_descriptor_filter(instance)
            if isinstance(extra_filter, dict):
                params.update(extra_filter)
                qs = qs.filter(**params)
            else:
                qs = qs.filter(extra_filter, **params)
            # Assuming the database enforces foreign keys, this won't fail.
            rel_obj = qs.get()
            if not self.field.rel.multiple:
                setattr(rel_obj, self.field.related.get_cache_name(), instance)
        setattr(instance, self.cache_name, rel_obj)
    if rel_obj is None and not self.field.null:
        raise self.RelatedObjectDoesNotExist(
            "%s has no %s." % (self.field.model.__name__, self.field.name)
        )
    else:
        return rel_obj

The problem is that this code:

    try:
        val = getattr(obj, attr_name)
    except related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist:
        val = None  # Does not catch the thrown exception
    except Exception as foo:
        print type(foo)  # Catches here, not above

won’t catch that exception

>>>print type(foo)
<class 'django.db.models.fields.related.RelatedObjectDoesNotExist'>
>>>isinstance(foo, related.FieldDoesNotExist)
False

and

except related.RelatedObjectDoesNotExist:

Raises an AttributeError: 'module' object has no attribute 'RelatedObjectDoesNotExist'

>>>isinstance(foo, related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist)
Traceback (most recent call last):
  File "<string>", line 1, in <fragment>
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

which is probably why.


回答 0

如果您的相关模型称为Foo,则可以执行以下操作:

except Foo.DoesNotExist:

Django令人惊叹,但并不可怕。RelatedObjectDoesNotExist是一个属性,该属性返回在运行时动态确定的类型。该类型self.field.rel.to.DoesNotExist用作基类。根据Django文档:

对象不存在和不存在

异常DowsNotExist

DoesNotExist时未找到查询的给定参数的对象异常。Django提供了DidNotExist 异常作为每个模型类的属性,以标识找不到的对象类,并允许您使用try/except捕获特定的模型类。

这就是使之成为现实的魔力。一旦建立了模型,该模型self.field.rel.to.DoesNotExist就是不存在的异常。

If your related model is called Foo you can just do:

except Foo.DoesNotExist:

Django is amazing when its not terrifying. RelatedObjectDoesNotExist is a property that returns a type that is figured out dynamically at runtime. That type uses self.field.rel.to.DoesNotExist as a base class. According to Django documentation:

ObjectDoesNotExist and DoesNotExist

exception DoesNotExist

The DoesNotExist exception is raised when an object is not found for the given parameters of a query. Django provides a DoesNotExist exception as an attribute of each model class to identify the class of object that could not be found and to allow you to catch a particular model class with try/except.

This is the magic that makes that happen. Once the model has been built up, self.field.rel.to.DoesNotExist is the does-not-exist exception for that model.


回答 1

如果您不想导入相关的模型类,则可以:

except MyModel.related_field.RelatedObjectDoesNotExist:

要么

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

哪里 related_field字段名称。

If you don’t want to import the related model class, you can:

except MyModel.related_field.RelatedObjectDoesNotExist:

or

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

where related_field is the field name.


回答 2

要捕获此异常,通常可以

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception

To catch this exception in general, you can do

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception

回答 3

RelatedObjectDoesNotExist在运行时动态创建的exceptions。以下是ForwardManyToOneDescriptorReverseOneToOneDescriptor描述符的相关代码段:

@cached_property
def RelatedObjectDoesNotExist(self):
    # The exception can't be created at initialization time since the
    # related model might not be resolved yet; `self.field.model` might
    # still be a string model reference.
    return type(
        'RelatedObjectDoesNotExist',
        (self.field.remote_field.model.DoesNotExist, AttributeError),
        {}
    )

因此,异常继承自<model name>.DoesNotExistAttributeError。实际上,此异常类型的完整MRO为:

[<class 'django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist'>, 
<class '<model module path>.DoesNotExist'>,
<class 'django.core.exceptions.ObjectDoesNotExist'>,
<class 'AttributeError'>,
<class 'Exception'>,
<class 'BaseException'>,
<class 'object'>]

基本的要点是您可以捕获<model name>.DoesNotExistObjectDoesNotExist(从导入django.core.exceptions)或AttributeError,在您的上下文中最有意义的。

The RelatedObjectDoesNotExist exception is created dynamically at runtime. Here is the relevant code snippet for the ForwardManyToOneDescriptor and ReverseOneToOneDescriptor descriptors:

@cached_property
def RelatedObjectDoesNotExist(self):
    # The exception can't be created at initialization time since the
    # related model might not be resolved yet; `self.field.model` might
    # still be a string model reference.
    return type(
        'RelatedObjectDoesNotExist',
        (self.field.remote_field.model.DoesNotExist, AttributeError),
        {}
    )

So the exception inherits from <model name>.DoesNotExist and AttributeError. In fact, the complete MRO for this exception type is:

[<class 'django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist'>, 
<class '<model module path>.DoesNotExist'>,
<class 'django.core.exceptions.ObjectDoesNotExist'>,
<class 'AttributeError'>,
<class 'Exception'>,
<class 'BaseException'>,
<class 'object'>]

The basic takeaway is you can catch <model name>.DoesNotExist, ObjectDoesNotExist (import from django.core.exceptions) or AttributeError, whatever makes the most sense in your context.


回答 4

tdelaney的答案非常适合常规代码路径,但是如果您需要知道如何在测试中捕获此异常,则:

from django.core.exceptions import ObjectDoesNotExist

...

    def testCompanyRequired(self):
        with self.assertRaises(ObjectDoesNotExist):
            employee = Employee.objects.create()

tdelaney’s answer is great for regular code paths, but if you need to know how to catch this exception in tests:

from django.core.exceptions import ObjectDoesNotExist

...

    def testCompanyRequired(self):
        with self.assertRaises(ObjectDoesNotExist):
            employee = Employee.objects.create()

回答 5

有点晚了,但对其他人有帮助。

有两种处理方法。

第一:

当我们需要捕获异常时

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

第二: 当不想处理异常时

>>> hasattr(p2, 'restaurant')
False

Little bit late but helpful for others.

2 ways to handle this.

1st :

When we need to catch exception

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

2nd: When don’t want to handle exception

>>> hasattr(p2, 'restaurant')
False