标签归档:Django

Django安装程序默认日志记录

问题:Django安装程序默认日志记录

我似乎无法弄清楚如何为Django安装设置“默认”记录器。我想在中使用Django 1.3的新LOGGING设置settings.py

我看过Django Logging Doc的示例,但在我看来,他们只设置了将为特定记录器记录日志的处理程序。在他们的示例中,他们为名为“ django”,“ django.request”和“ myproject.custom”的记录器设置了处理程序。

我要做的只是设置一个默认值logging.handlers.RotatingFileHandler,它将默认处理所有记录器。即,如果我在项目中的某个地方创建了一个新模块,并用如下符号表示:my_app_name.my_new_module,我应该能够做到这一点,并将所有日志记录转到旋转文件日志中。

# In file './my_app_name/my_new_module.py'
import logging
logger = logging.getLogger('my_app_name.my_new_module')
logger.debug('Hello logs!') # <-- This should get logged to my RotatingFileHandler that I setup in `settings.py`!

I can’t seem to figure out how to setup a “default” logger for my Django installation. I would like to use Django 1.3’s new LOGGING setting in settings.py.

I’ve looked at the Django Logging Doc’s example, but it looks to me like they only setup handlers which will do logging for particular loggers. In the case of their example they setup handler for the loggers named ‘django’,’django.request’, and ‘myproject.custom’.

All I want to do is setup a default logging.handlers.RotatingFileHandler which will handle all loggers by default. i.e., if I make a new module somewhere in my project and it is denoted by something like: my_app_name.my_new_module, I should be able to do this and have all logging goto the rotating file logs.

# In file './my_app_name/my_new_module.py'
import logging
logger = logging.getLogger('my_app_name.my_new_module')
logger.debug('Hello logs!') # <-- This should get logged to my RotatingFileHandler that I setup in `settings.py`!

回答 0

弄清楚了…

您可以使用空字符串引用来设置“全部捕获”记录器''

举例来说,在以下设置中,我将所有日志事件保存到logs/mylog.log,除了django.request日志事件将保存到logs/django_request.log。因为'propagate'设置False为我的django.request记录器,所以日志事件将永远不会到达“全部捕获”记录器。

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'default': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/mylog.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },  
        'request_handler': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/django_request.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },
    },
    'loggers': {
        '': {
            'handlers': ['default'],
            'level': 'DEBUG',
            'propagate': True
        },
        'django.request': {
            'handlers': ['request_handler'],
            'level': 'DEBUG',
            'propagate': False
        },
    }
}

Figured it out…

You set the ‘catch all’ logger by referencing it with the empty string: ''.

As an example, in the following setup I have the all log events getting saved to logs/mylog.log, with the exception of django.request log events which will be saved to logs/django_request.log. Because 'propagate' is set to False for my django.request logger, the log event will never reach the the ‘catch all’ logger.

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'default': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/mylog.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },  
        'request_handler': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/django_request.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },
    },
    'loggers': {
        '': {
            'handlers': ['default'],
            'level': 'DEBUG',
            'propagate': True
        },
        'django.request': {
            'handlers': ['request_handler'],
            'level': 'DEBUG',
            'propagate': False
        },
    }
}

回答 1

正如您在回答 Chris中所说,定义默认记录器的一种方法是使用空字符串作为其键。

但是,我认为预期的方法是root在日志记录配置字典的键下定义一个特殊的日志记录器。我在Python文档中找到了这个:

root-这将是root记录器的配置。配置的处理将与所有记录器相同,但该propagate设置将不适用。

这是您的答案更改为使用root键后的配置:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'default': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/mylog.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },  
        'request_handler': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/django_request.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },
    },
    'root': {
        'handlers': ['default'],
        'level': 'DEBUG'
    },
    'loggers': {
        'django.request': {
            'handlers': ['request_handler'],
            'level': 'DEBUG',
            'propagate': False
        },
    }
}

公平地说,我看不到这两种配置在行为上有任何区别。似乎使用空字符串键定义记录器将修改根记录器,因为logging.getLogger('')它将返回根记录器。

我比较喜欢的唯一原因'root'''在于它是明确有关修改根记录。如果您很好奇,请定义两个都'root'覆盖'',这是因为根条目是最后处理的。

As you said in your answer, Chris, one option to define a default logger is to use the empty string as its key.

However, I think the intended way is to define a special logger under the root key of the logging configuration dictionary. I found this in the Python documentation:

root – this will be the configuration for the root logger. Processing of the configuration will be as for any logger, except that the propagate setting will not be applicable.

Here’s the configuration from your answer changed to use the root key:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
    },
    'handlers': {
        'default': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/mylog.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },  
        'request_handler': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': 'logs/django_request.log',
            'maxBytes': 1024*1024*5, # 5 MB
            'backupCount': 5,
            'formatter':'standard',
        },
    },
    'root': {
        'handlers': ['default'],
        'level': 'DEBUG'
    },
    'loggers': {
        'django.request': {
            'handlers': ['request_handler'],
            'level': 'DEBUG',
            'propagate': False
        },
    }
}

To be fair, I can’t see any difference in behaviour between the two configurations. It appears that defining a logger with an empty string key will modify the root logger, because logging.getLogger('') will return the root logger.

The only reason I prefer 'root' over '' is that it is explicit about modifying the root logger. In case you were curious, 'root' overrides '' if you define both, just because the root entry is processed last.


回答 2

import logging
logger = logging.getLogger(__name__)

添加后:

logging.basicConfig(
    level = logging.DEBUG,
    format = '%(name)s %(levelname)s %(message)s',
)

我们可以将格式更改为:

format = '"%(levelname)s:%(name)s:%(message)s"  ',

要么

format = '%(name)s %(asctime)s %(levelname)s %(message)s',
import logging
logger = logging.getLogger(__name__)

after add:

logging.basicConfig(
    level = logging.DEBUG,
    format = '%(name)s %(levelname)s %(message)s',
)

we may change format to:

format = '"%(levelname)s:%(name)s:%(message)s"  ',

or

format = '%(name)s %(asctime)s %(levelname)s %(message)s',

回答 3

我做了一个快速示例来检查在config dict中同时引用rootkey和empty ''logger时使用的配置。

import logging.config

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'fmt1': {
            'format': '[FMT1] %(asctime)-15s %(message)s',
        },
        'fmt2': {
            'format': '[FMT2] %(asctime)-15s %(message)s',
        }
    },
    'handlers': {
        'console1': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'fmt1',
        },
        'console2': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'fmt2',
        },
    },
    # First config for root logger: console1 -> fmt1
    'root': {
        'handlers': ['console1'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'loggers': {
        # Second config for root logger: console2 -> fmt2
        '': {
            'handlers': ['console2'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

logging.config.dictConfig(LOGGING)

l1 = logging.getLogger()
l2 = logging.getLogger('')
root = logging.root

l1.info("l1")
l2.info("l2")
root.info("root logger")

打印以下结果:

[FMT1] 2018-12-18 17:24:47,691 l1
[FMT1] 2018-12-18 17:24:47,691 l2
[FMT1] 2018-12-18 17:24:47,691 root logger

表示root密钥下的配置具有最高优先级。如果删除了该块,则结果为:

[FMT2] 2018-12-18 17:25:43,757 l1
[FMT2] 2018-12-18 17:25:43,757 l2
[FMT2] 2018-12-18 17:25:43,757 root logger

在这两种情况下,我能够调试并确定所有三个记录器(l1l2root)引用相同的记录器实例,根记录器。

希望这将对像我一样对配置根记录程序的两种不同方式感到困惑的其他人有所帮助。

I made a quick sample to check what configuration is used when both root key and the empty '' logger are referenced in config dict.

import logging.config

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'fmt1': {
            'format': '[FMT1] %(asctime)-15s %(message)s',
        },
        'fmt2': {
            'format': '[FMT2] %(asctime)-15s %(message)s',
        }
    },
    'handlers': {
        'console1': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'fmt1',
        },
        'console2': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'fmt2',
        },
    },
    # First config for root logger: console1 -> fmt1
    'root': {
        'handlers': ['console1'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'loggers': {
        # Second config for root logger: console2 -> fmt2
        '': {
            'handlers': ['console2'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

logging.config.dictConfig(LOGGING)

l1 = logging.getLogger()
l2 = logging.getLogger('')
root = logging.root

l1.info("l1")
l2.info("l2")
root.info("root logger")

Prints the following result:

[FMT1] 2018-12-18 17:24:47,691 l1
[FMT1] 2018-12-18 17:24:47,691 l2
[FMT1] 2018-12-18 17:24:47,691 root logger

indicating that configuration under root key has the highest priority. If the block is removed, the result is:

[FMT2] 2018-12-18 17:25:43,757 l1
[FMT2] 2018-12-18 17:25:43,757 l2
[FMT2] 2018-12-18 17:25:43,757 root logger

In both case, I was able to debug and determine that all three loggers (l1, l2 and root) referenced the same logger instance, the root logger.

Hope that will help others who, like me, were confused by the 2 different ways to configure the root logger.


Django admin中的默认过滤器

问题:Django admin中的默认过滤器

如何从“全部”更改默认过滤器选择?我有一个名为领域status它有三个值:activatependingrejected。当我list_filter在Django admin中使用时,过滤器默认情况下设置为“全部”,但我想默认将其设置为待处理。

How can I change the default filter choice from ‘ALL’? I have a field named as status which has three values: activate, pending and rejected. When I use list_filter in Django admin, the filter is by default set to ‘All’ but I want to set it to pending by default.


回答 0

为了实现这一点,在侧边栏中有一个可用的“全部”链接(即显示全部而不显示未决的链接),您需要创建一个自定义列表过滤器,django.contrib.admin.filters.SimpleListFilter默认情况下继承并过滤“待处理”。遵循这些原则的方法应该起作用:

from datetime import date

from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter

class StatusFilter(SimpleListFilter):
    title = _('Status')

    parameter_name = 'status'

    def lookups(self, request, model_admin):
        return (
            (None, _('Pending')),
            ('activate', _('Activate')),
            ('rejected', _('Rejected')),
            ('all', _('All')),
        )

    def choices(self, cl):
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == lookup,
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        if self.value() in ('activate', 'rejected'):
            return queryset.filter(status=self.value())    
        elif self.value() == None:
            return queryset.filter(status='pending')


class Admin(admin.ModelAdmin): 
    list_filter = [StatusFilter] 

编辑:需要Django 1.4(感谢Simon)

In order to achieve this and have a usable ‘All’ link in your sidebar (ie one that shows all rather than showing pending), you’d need to create a custom list filter, inheriting from django.contrib.admin.filters.SimpleListFilter and filtering on ‘pending’ by default. Something along these lines should work:

from datetime import date

from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter

class StatusFilter(SimpleListFilter):
    title = _('Status')

    parameter_name = 'status'

    def lookups(self, request, model_admin):
        return (
            (None, _('Pending')),
            ('activate', _('Activate')),
            ('rejected', _('Rejected')),
            ('all', _('All')),
        )

    def choices(self, cl):
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == lookup,
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        if self.value() in ('activate', 'rejected'):
            return queryset.filter(status=self.value())    
        elif self.value() == None:
            return queryset.filter(status='pending')


class Admin(admin.ModelAdmin): 
    list_filter = [StatusFilter] 

EDIT: Requires Django 1.4 (thanks Simon)


回答 1

class MyModelAdmin(admin.ModelAdmin):   

    def changelist_view(self, request, extra_context=None):

        if not request.GET.has_key('decommissioned__exact'):

            q = request.GET.copy()
            q['decommissioned__exact'] = 'N'
            request.GET = q
            request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)
class MyModelAdmin(admin.ModelAdmin):   

    def changelist_view(self, request, extra_context=None):

        if not request.GET.has_key('decommissioned__exact'):

            q = request.GET.copy()
            q['decommissioned__exact'] = 'N'
            request.GET = q
            request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

回答 2

接受了ha22109的解答,并进行了修改,以允许通过比较HTTP_REFERER和选择“全部” PATH_INFO

class MyModelAdmin(admin.ModelAdmin):

    def changelist_view(self, request, extra_context=None):

        test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO'])

        if test[-1] and not test[-1].startswith('?'):
            if not request.GET.has_key('decommissioned__exact'):

                q = request.GET.copy()
                q['decommissioned__exact'] = 'N'
                request.GET = q
                request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

Took ha22109’s answer above and modified to allow the selection of “All” by comparing HTTP_REFERER and PATH_INFO.

class MyModelAdmin(admin.ModelAdmin):

    def changelist_view(self, request, extra_context=None):

        test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO'])

        if test[-1] and not test[-1].startswith('?'):
            if not request.GET.has_key('decommissioned__exact'):

                q = request.GET.copy()
                q['decommissioned__exact'] = 'N'
                request.GET = q
                request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

回答 3

我知道这个问题现在已经很老了,但仍然有效。我相信这是最正确的方法。它本质上与Greg的方法相同,但公式化为可扩展的类,以方便重用。

from django.contrib.admin import SimpleListFilter
from django.utils.encoding import force_text
from django.utils.translation import ugettext as _

class DefaultListFilter(SimpleListFilter):
    all_value = '_all'

    def default_value(self):
        raise NotImplementedError()

    def queryset(self, request, queryset):
        if self.parameter_name in request.GET and request.GET[self.parameter_name] == self.all_value:
            return queryset

        if self.parameter_name in request.GET:
            return queryset.filter(**{self.parameter_name:request.GET[self.parameter_name]})

        return queryset.filter(**{self.parameter_name:self.default_value()})

    def choices(self, cl):
        yield {
            'selected': self.value() == self.all_value,
            'query_string': cl.get_query_string({self.parameter_name: self.all_value}, []),
            'display': _('All'),
        }
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == force_text(lookup) or (self.value() == None and force_text(self.default_value()) == force_text(lookup)),
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

class StatusFilter(DefaultListFilter):
    title = _('Status ')
    parameter_name = 'status__exact'

    def lookups(self, request, model_admin):
        return ((0,'activate'), (1,'pending'), (2,'rejected'))

    def default_value(self):
        return 1

class MyModelAdmin(admin.ModelAdmin):
    list_filter = (StatusFilter,)

I know this question is quite old now, but it’s still valid. I believe this is the most correct way of doing this. It’s essentially the same as Greg’s method, but formulated as an extendible class for easy re-use.

from django.contrib.admin import SimpleListFilter
from django.utils.encoding import force_text
from django.utils.translation import ugettext as _

class DefaultListFilter(SimpleListFilter):
    all_value = '_all'

    def default_value(self):
        raise NotImplementedError()

    def queryset(self, request, queryset):
        if self.parameter_name in request.GET and request.GET[self.parameter_name] == self.all_value:
            return queryset

        if self.parameter_name in request.GET:
            return queryset.filter(**{self.parameter_name:request.GET[self.parameter_name]})

        return queryset.filter(**{self.parameter_name:self.default_value()})

    def choices(self, cl):
        yield {
            'selected': self.value() == self.all_value,
            'query_string': cl.get_query_string({self.parameter_name: self.all_value}, []),
            'display': _('All'),
        }
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == force_text(lookup) or (self.value() == None and force_text(self.default_value()) == force_text(lookup)),
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

class StatusFilter(DefaultListFilter):
    title = _('Status ')
    parameter_name = 'status__exact'

    def lookups(self, request, model_admin):
        return ((0,'activate'), (1,'pending'), (2,'rejected'))

    def default_value(self):
        return 1

class MyModelAdmin(admin.ModelAdmin):
    list_filter = (StatusFilter,)

回答 4

这是我使用重定向的通用解决方案,它仅检查是否有任何GET参数,如果不存在,则使用默认的get参数进行重定向。我还设置了一个list_filter,因此它将其选中并显示默认值。

from django.shortcuts import redirect

class MyModelAdmin(admin.ModelAdmin):   

    ...

    list_filter = ('status', )

    def changelist_view(self, request, extra_context=None):
        referrer = request.META.get('HTTP_REFERER', '')
        get_param = "status__exact=5"
        if len(request.GET) == 0 and '?' not in referrer:
            return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param))
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

唯一需要注意的是,当您使用“?”直接进入页面时 网址中没有设置HTTP_REFERER,因此它将使用默认参数并重定向。这对我来说很好,当您单击管理过滤器时,效果很好。

更新

为了解决这个问题,我最终编写了一个自定义过滤器函数,该函数简化了changelist_view功能。这是过滤器:

class MyModelStatusFilter(admin.SimpleListFilter):
    title = _('Status')
    parameter_name = 'status'

    def lookups(self, request, model_admin):  # Available Values / Status Codes etc..
        return (
            (8, _('All')),
            (0, _('Incomplete')),
            (5, _('Pending')),
            (6, _('Selected')),
            (7, _('Accepted')),
        )

    def choices(self, cl):  # Overwrite this method to prevent the default "All"
        from django.utils.encoding import force_text
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == force_text(lookup),
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):  # Run the queryset based on your lookup values
        if self.value() is None:
            return queryset.filter(status=5)
        elif int(self.value()) == 0:
            return queryset.filter(status__lte=4)
        elif int(self.value()) == 8:
            return queryset.all()
        elif int(self.value()) >= 5:
            return queryset.filter(status=self.value())
        return queryset.filter(status=5)

现在,changelist_view仅在不存在默认参数的情况下才传递默认参数。这个想法是通过不使用get参数来摆脱泛型过滤器查看所有内容的功能。要查看全部,为此我分配了状态= 8。

class MyModelAdmin(admin.ModelAdmin):   

    ...

    list_filter = ('status', )

    def changelist_view(self, request, extra_context=None):
        if len(request.GET) == 0:
            get_param = "status=5"
            return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param))
        return super(MyModelAdmin, self).changelist_view(request, extra_context=extra_context)

Here is my generic solution using redirect, it just checks if there are any GET parameters, if none exist then it redirects with the default get parameter. I also have a list_filter set so it picks that up and displays the default.

from django.shortcuts import redirect

class MyModelAdmin(admin.ModelAdmin):   

    ...

    list_filter = ('status', )

    def changelist_view(self, request, extra_context=None):
        referrer = request.META.get('HTTP_REFERER', '')
        get_param = "status__exact=5"
        if len(request.GET) == 0 and '?' not in referrer:
            return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param))
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

The only caveat is when you do a direct get to the page with “?” present in the url, there is no HTTP_REFERER set so it will use the default parameter and redirect. This is fine for me, it works great when you click through the admin filter.

UPDATE:

In order to get around the caveat, I ended up writing a custom filter function which simplified the changelist_view functionality. Here is the filter:

class MyModelStatusFilter(admin.SimpleListFilter):
    title = _('Status')
    parameter_name = 'status'

    def lookups(self, request, model_admin):  # Available Values / Status Codes etc..
        return (
            (8, _('All')),
            (0, _('Incomplete')),
            (5, _('Pending')),
            (6, _('Selected')),
            (7, _('Accepted')),
        )

    def choices(self, cl):  # Overwrite this method to prevent the default "All"
        from django.utils.encoding import force_text
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == force_text(lookup),
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):  # Run the queryset based on your lookup values
        if self.value() is None:
            return queryset.filter(status=5)
        elif int(self.value()) == 0:
            return queryset.filter(status__lte=4)
        elif int(self.value()) == 8:
            return queryset.all()
        elif int(self.value()) >= 5:
            return queryset.filter(status=self.value())
        return queryset.filter(status=5)

And the changelist_view now only passes the default parameter if none are present. The idea was to get rid of the generics filters capability to view all by using no get parameters. To view all I assigned the status = 8 for that purpose.:

class MyModelAdmin(admin.ModelAdmin):   

    ...

    list_filter = ('status', )

    def changelist_view(self, request, extra_context=None):
        if len(request.GET) == 0:
            get_param = "status=5"
            return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param))
        return super(MyModelAdmin, self).changelist_view(request, extra_context=extra_context)

回答 5

def changelist_view( self, request, extra_context = None ):
    default_filter = False
    try:
        ref = request.META['HTTP_REFERER']
        pinfo = request.META['PATH_INFO']
        qstr = ref.split( pinfo )

        if len( qstr ) < 2:
            default_filter = True
    except:
        default_filter = True

    if default_filter:
        q = request.GET.copy()
        q['registered__exact'] = '1'
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super( InterestAdmin, self ).changelist_view( request, extra_context = extra_context )
def changelist_view( self, request, extra_context = None ):
    default_filter = False
    try:
        ref = request.META['HTTP_REFERER']
        pinfo = request.META['PATH_INFO']
        qstr = ref.split( pinfo )

        if len( qstr ) < 2:
            default_filter = True
    except:
        default_filter = True

    if default_filter:
        q = request.GET.copy()
        q['registered__exact'] = '1'
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super( InterestAdmin, self ).changelist_view( request, extra_context = extra_context )

回答 6

您可以简单地使用return queryset.filter()if self.value() is None重写SimpleListFilter方法

from django.utils.encoding import force_text

def choices(self, changelist):
    for lookup, title in self.lookup_choices:
        yield {
            'selected': force_text(self.value()) == force_text(lookup),
            'query_string': changelist.get_query_string(
                {self.parameter_name: lookup}, []
            ),
            'display': title,
        }

You can simply usereturn queryset.filter() or if self.value() is None and Override method of SimpleListFilter

from django.utils.encoding import force_text

def choices(self, changelist):
    for lookup, title in self.lookup_choices:
        yield {
            'selected': force_text(self.value()) == force_text(lookup),
            'query_string': changelist.get_query_string(
                {self.parameter_name: lookup}, []
            ),
            'display': title,
        }

回答 7

请注意,如果您不想始终选择过滤器值,而是希望始终在对数据进行管理之前对其进行预过滤,则应改写该ModelAdmin.queryset()方法。

Note that if instead of pre-selecting a filter value you want to always pre-filter the data before showing it in the admin, you should override the ModelAdmin.queryset() method instead.


回答 8

Greg使用DjangoChoices,Python> = 2.5,当然还有Django> = 1.4的答案略有改进。

from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter

class OrderStatusFilter(SimpleListFilter):
    title = _('Status')

    parameter_name = 'status__exact'
    default_status = OrderStatuses.closed

    def lookups(self, request, model_admin):
        return (('all', _('All')),) + OrderStatuses.choices

    def choices(self, cl):
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == lookup if self.value() else lookup == self.default_status,
                'query_string': cl.get_query_string({self.parameter_name: lookup}, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        if self.value() in OrderStatuses.values:
            return queryset.filter(status=self.value())
        elif self.value() is None:
            return queryset.filter(status=self.default_status)


class Admin(admin.ModelAdmin):
    list_filter = [OrderStatusFilter] 

感谢格雷格提供了不错的解决方案!

A slight improvement on Greg’s answer using DjangoChoices, Python >= 2.5 and of course Django >= 1.4.

from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import SimpleListFilter

class OrderStatusFilter(SimpleListFilter):
    title = _('Status')

    parameter_name = 'status__exact'
    default_status = OrderStatuses.closed

    def lookups(self, request, model_admin):
        return (('all', _('All')),) + OrderStatuses.choices

    def choices(self, cl):
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == lookup if self.value() else lookup == self.default_status,
                'query_string': cl.get_query_string({self.parameter_name: lookup}, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        if self.value() in OrderStatuses.values:
            return queryset.filter(status=self.value())
        elif self.value() is None:
            return queryset.filter(status=self.default_status)


class Admin(admin.ModelAdmin):
    list_filter = [OrderStatusFilter] 

Thanks to Greg for the nice solution!


回答 9

我知道这不是最好的解决方案,但是我在管理模板的第25行和第37行中更改了index.html,如下所示:

25: <th scope="row"><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag_flag__exact=1{% endifequal %}">{{ model.name }}</a></th>

37: <td><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag__exact=1{% endifequal %}" class="changelink">{% trans 'Change' %}</a></td>

I know that is not the best solution, but i changed the index.html in the admin template, line 25 and 37 like this:

25: <th scope="row"><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag_flag__exact=1{% endifequal %}">{{ model.name }}</a></th>

37: <td><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag__exact=1{% endifequal %}" class="changelink">{% trans 'Change' %}</a></td>


回答 10

我必须进行修改才能使过滤正常工作。页面加载时,先前的解决方案对我有用。如果执行了“操作”,则过滤器将返回“全部”,而不是我的默认设置。此解决方案使用默认过滤器加载管理更改页面,但是当页面上发生其他活动时,也可以维护过滤器更改或当前过滤器。我没有测试所有情况,但实际上,它可能限制了默认过滤器的设置,使其仅在页面加载时才发生。

def changelist_view(self, request, extra_context=None):
    default_filter = False

    try:
        ref = request.META['HTTP_REFERER']
        pinfo = request.META['PATH_INFO']
        qstr = ref.split(pinfo)
        querystr = request.META['QUERY_STRING']

        # Check the QUERY_STRING value, otherwise when
        # trying to filter the filter gets reset below
        if querystr is None:
            if len(qstr) < 2 or qstr[1] == '':
                default_filter = True
    except:
        default_filter = True

    if default_filter:
        q = request.GET.copy()
        q['registered__isnull'] = 'True'
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super(MyAdmin, self).changelist_view(request, extra_context=extra_context)

I had to make a modification to get filtering to work correctly. The previous solution worked for me when the page loaded. If an ‘action’ was performed, the filter went back to ‘All’ and not my default. This solution loads the admin change page with the default filter, but also maintains filter changes or the current filter when other activity occurs on the page. I haven’t tested all cases, but in reality it may be limiting the setting of a default filter to occur only when the page loads.

def changelist_view(self, request, extra_context=None):
    default_filter = False

    try:
        ref = request.META['HTTP_REFERER']
        pinfo = request.META['PATH_INFO']
        qstr = ref.split(pinfo)
        querystr = request.META['QUERY_STRING']

        # Check the QUERY_STRING value, otherwise when
        # trying to filter the filter gets reset below
        if querystr is None:
            if len(qstr) < 2 or qstr[1] == '':
                default_filter = True
    except:
        default_filter = True

    if default_filter:
        q = request.GET.copy()
        q['registered__isnull'] = 'True'
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super(MyAdmin, self).changelist_view(request, extra_context=extra_context)

回答 11

有点题外话,但我对类似问题的搜索将我引到了这里。我希望按日期进行默认查询(即,如果未提供任何输入,则仅显示带有timestamp“ Today”的对象),这使问题变得有些复杂。这是我想出的:

from django.contrib.admin.options import IncorrectLookupParameters
from django.core.exceptions import ValidationError

class TodayDefaultDateFieldListFilter(admin.DateFieldListFilter):
    """ If no date is query params are provided, query for Today """

    def queryset(self, request, queryset):
        try:
            if not self.used_parameters:
                now = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
                self.used_parameters = {
                    ('%s__lt' % self.field_path): str(now + datetime.timedelta(days=1)),
                    ('%s__gte' % self.field_path): str(now),
                }
                # Insure that the dropdown reflects 'Today'
                self.date_params = self.used_parameters
            return queryset.filter(**self.used_parameters)
        except ValidationError, e:
            raise IncorrectLookupParameters(e)

class ImagesAdmin(admin.ModelAdmin):
    list_filter = (
        ('timestamp', TodayDefaultDateFieldListFilter),
    )

这是对default的简单覆盖DateFieldListFilter。通过设置self.date_params,可以确保过滤器下拉列表将更新为与匹配的任何选项self.used_parameters。出于这个原因,您必须确保self.used_parameters下拉列表选择项中的一个恰好会使用。(即,找出date_params使用“今天”或“过去7天”时的含义,并构造与之self.used_parameters匹配的)。

旨在与Django 1.4.10一起使用

A bit off-topic but my search for a similar question led me here. I was looking to have a default query by a date (ie if no input is provided, show only objects with timestamp of ‘Today’), which complicates the question a bit. Here is what I came up with:

from django.contrib.admin.options import IncorrectLookupParameters
from django.core.exceptions import ValidationError

class TodayDefaultDateFieldListFilter(admin.DateFieldListFilter):
    """ If no date is query params are provided, query for Today """

    def queryset(self, request, queryset):
        try:
            if not self.used_parameters:
                now = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
                self.used_parameters = {
                    ('%s__lt' % self.field_path): str(now + datetime.timedelta(days=1)),
                    ('%s__gte' % self.field_path): str(now),
                }
                # Insure that the dropdown reflects 'Today'
                self.date_params = self.used_parameters
            return queryset.filter(**self.used_parameters)
        except ValidationError, e:
            raise IncorrectLookupParameters(e)

class ImagesAdmin(admin.ModelAdmin):
    list_filter = (
        ('timestamp', TodayDefaultDateFieldListFilter),
    )

This is a simple override of the default DateFieldListFilter. By setting self.date_params, it insures that the filter dropdown will update to whatever option matches the self.used_parameters. For this reason, you must insure that the self.used_parameters are exactly what would be used by one of those dropdown selections (ie, find out what the date_params would be when using the ‘Today’ or ‘Last 7 Days’ and construct the self.used_parameters to match those).

This was built to work with Django 1.4.10


回答 12

这可能是一个旧线程,但是我想添加我的解决方案,因为我无法在Google搜索中找到更好的答案。

在ModelAdmin中为changelist_view做什么(不确定其Deminic Rodger还是ha22109)

class MyModelAdmin(admin.ModelAdmin):   
    list_filter = (CustomFilter,)

    def changelist_view(self, request, extra_context=None):

        if not request.GET.has_key('decommissioned__exact'):

            q = request.GET.copy()
            q['decommissioned__exact'] = 'N'
            request.GET = q
            request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

然后我们需要创建一个自定义的SimpleListFilter

class CustomFilter(admin.SimpleListFilter):
    title = 'Decommissioned'
    parameter_name = 'decommissioned'  # i chose to change it

def lookups(self, request, model_admin):
    return (
        ('All', 'all'),
        ('1', 'Decommissioned'),
        ('0', 'Active (or whatever)'),
    )

# had to override so that we could remove the default 'All' option
# that won't work with our default filter in the ModelAdmin class
def choices(self, cl):
    yield {
        'selected': self.value() is None,
        'query_string': cl.get_query_string({}, [self.parameter_name]),
        # 'display': _('All'),
    }
    for lookup, title in self.lookup_choices:
        yield {
            'selected': self.value() == lookup,
            'query_string': cl.get_query_string({
                self.parameter_name: lookup,
            }, []),
            'display': title,
        }

def queryset(self, request, queryset):
    if self.value() == '1':
        return queryset.filter(decommissioned=1)
    elif self.value() == '0':
        return queryset.filter(decommissioned=0)
    return queryset

This may be an old thread, but thought I would add my solution as I couldn’t find better answers on google searches.

Do what (not sure if its Deminic Rodger, or ha22109) answered in the ModelAdmin for changelist_view

class MyModelAdmin(admin.ModelAdmin):   
    list_filter = (CustomFilter,)

    def changelist_view(self, request, extra_context=None):

        if not request.GET.has_key('decommissioned__exact'):

            q = request.GET.copy()
            q['decommissioned__exact'] = 'N'
            request.GET = q
            request.META['QUERY_STRING'] = request.GET.urlencode()
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)

Then we need to create a custom SimpleListFilter

class CustomFilter(admin.SimpleListFilter):
    title = 'Decommissioned'
    parameter_name = 'decommissioned'  # i chose to change it

def lookups(self, request, model_admin):
    return (
        ('All', 'all'),
        ('1', 'Decommissioned'),
        ('0', 'Active (or whatever)'),
    )

# had to override so that we could remove the default 'All' option
# that won't work with our default filter in the ModelAdmin class
def choices(self, cl):
    yield {
        'selected': self.value() is None,
        'query_string': cl.get_query_string({}, [self.parameter_name]),
        # 'display': _('All'),
    }
    for lookup, title in self.lookup_choices:
        yield {
            'selected': self.value() == lookup,
            'query_string': cl.get_query_string({
                self.parameter_name: lookup,
            }, []),
            'display': title,
        }

def queryset(self, request, queryset):
    if self.value() == '1':
        return queryset.filter(decommissioned=1)
    elif self.value() == '0':
        return queryset.filter(decommissioned=0)
    return queryset

回答 13

这是我能够生成的具有筛选器的最干净的版本,该筛选器具有重新定义的“全部”和已选择的默认值。

如果默认情况下显示我,则当前正在发生的旅行。

class HappeningTripFilter(admin.SimpleListFilter):
    """
    Filter the Trips Happening in the Past, Future or now.
    """
    default_value = 'now'
    title = 'Happening'
    parameter_name = 'happening'

    def lookups(self, request, model_admin):
        """
        List the Choices available for this filter.
        """
        return (
            ('all', 'All'),
            ('future', 'Not yet started'),
            ('now', 'Happening now'),
            ('past', 'Already finished'),
        )

    def choices(self, changelist):
        """
        Overwrite this method to prevent the default "All".
        """
        value = self.value() or self.default_value
        for lookup, title in self.lookup_choices:
            yield {
                'selected': value == force_text(lookup),
                'query_string': changelist.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        """
        Returns the Queryset depending on the Choice.
        """
        value = self.value() or self.default_value
        now = timezone.now()
        if value == 'future':
            return queryset.filter(start_date_time__gt=now)
        if value == 'now':
            return queryset.filter(start_date_time__lte=now, end_date_time__gte=now)
        if value == 'past':
            return queryset.filter(end_date_time__lt=now)
        return queryset.all()

Here’s the Cleanest version I was able to generate of a filter with a redefined ‘All’ and a Default value that is selected.

If shows me by default the Trips currently happening.

class HappeningTripFilter(admin.SimpleListFilter):
    """
    Filter the Trips Happening in the Past, Future or now.
    """
    default_value = 'now'
    title = 'Happening'
    parameter_name = 'happening'

    def lookups(self, request, model_admin):
        """
        List the Choices available for this filter.
        """
        return (
            ('all', 'All'),
            ('future', 'Not yet started'),
            ('now', 'Happening now'),
            ('past', 'Already finished'),
        )

    def choices(self, changelist):
        """
        Overwrite this method to prevent the default "All".
        """
        value = self.value() or self.default_value
        for lookup, title in self.lookup_choices:
            yield {
                'selected': value == force_text(lookup),
                'query_string': changelist.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        """
        Returns the Queryset depending on the Choice.
        """
        value = self.value() or self.default_value
        now = timezone.now()
        if value == 'future':
            return queryset.filter(start_date_time__gt=now)
        if value == 'now':
            return queryset.filter(start_date_time__lte=now, end_date_time__gte=now)
        if value == 'past':
            return queryset.filter(end_date_time__lt=now)
        return queryset.all()

回答 14

创建了一个可重用的Filter子类,其灵感来自此处的某些答案(主要是Greg的答案)。

优点:

可重复使用 -可插入任何标准ModelAdmin类别

可扩展 -易于添加其他/自定义逻辑进行QuerySet过滤

易于使用 -以其最基本的形式,只需实现一个自定义属性和一种自定义方法(除了SimpleListFilter子类所需的那些属性)

直观的管理员 -“所有”过滤器链接按预期工作;和所有其他人一样

无需重定向 -无需检查GET不可知的请求有效负载HTTP_REFERER(或其他任何与请求相关的内容,基本形式)

否(更改列表)视图操纵 -也没有模板操作(上帝禁止)

码:

(大多数imports仅用于类型提示和异常)

from typing import List, Tuple, Any

from django.contrib.admin.filters import SimpleListFilter
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList
from django.db.models.query import QuerySet
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError


class PreFilteredListFilter(SimpleListFilter):

    # Either set this or override .get_default_value()
    default_value = None

    no_filter_value = 'all'
    no_filter_name = _("All")

    # Human-readable title which will be displayed in the
    # right admin sidebar just above the filter options.
    title = None

    # Parameter for the filter that will be used in the URL query.
    parameter_name = None

    def get_default_value(self):
        if self.default_value is not None:
            return self.default_value
        raise NotImplementedError(
            'Either the .default_value attribute needs to be set or '
            'the .get_default_value() method must be overridden to '
            'return a URL query argument for parameter_name.'
        )

    def get_lookups(self) -> List[Tuple[Any, str]]:
        """
        Returns a list of tuples. The first element in each
        tuple is the coded value for the option that will
        appear in the URL query. The second element is the
        human-readable name for the option that will appear
        in the right sidebar.
        """
        raise NotImplementedError(
            'The .get_lookups() method must be overridden to '
            'return a list of tuples (value, verbose value).'
        )

    # Overriding parent class:
    def lookups(self, request, model_admin) -> List[Tuple[Any, str]]:
        return [(self.no_filter_value, self.no_filter_name)] + self.get_lookups()

    # Overriding parent class:
    def queryset(self, request, queryset: QuerySet) -> QuerySet:
        """
        Returns the filtered queryset based on the value
        provided in the query string and retrievable via
        `self.value()`.
        """
        if self.value() is None:
            return self.get_default_queryset(queryset)
        if self.value() == self.no_filter_value:
            return queryset.all()
        return self.get_filtered_queryset(queryset)

    def get_default_queryset(self, queryset: QuerySet) -> QuerySet:
        return queryset.filter(**{self.parameter_name: self.get_default_value()})

    def get_filtered_queryset(self, queryset: QuerySet) -> QuerySet:
        try:
            return queryset.filter(**self.used_parameters)
        except (ValueError, ValidationError) as e:
            # Fields may raise a ValueError or ValidationError when converting
            # the parameters to the correct type.
            raise IncorrectLookupParameters(e)

    # Overriding parent class:
    def choices(self, changelist: ChangeList):
        """
        Overridden to prevent the default "All".
        """
        value = self.value() or force_str(self.get_default_value())
        for lookup, title in self.lookup_choices:
            yield {
                'selected': value == force_str(lookup),
                'query_string': changelist.get_query_string({self.parameter_name: lookup}),
                'display': title,
            }

完整用法示例:

from django.contrib import admin
from .models import SomeModelWithStatus


class StatusFilter(PreFilteredListFilter):
    default_value = SomeModelWithStatus.Status.FOO
    title = _('Status')
    parameter_name = 'status'

    def get_lookups(self):
        return SomeModelWithStatus.Status.choices


@admin.register(SomeModelWithStatus)
class SomeModelAdmin(admin.ModelAdmin):
    list_filter = (StatusFilter, )

希望这对某人有帮助;反馈总是赞赏。

Created a reusable Filter sub-class, inspired by some of the answers here (mostly Greg’s).

Advantages:

Reusable – Pluggable in any standard ModelAdmin classes

Extendable – Easy to add additional/custom logic for QuerySet filtering

Easy to use – In its most basic form, only one custom attribute and one custom method need to be implemented (apart from those required for SimpleListFilter subclassing)

Intuitive admin – The “All” filter link is working as expected; as are all the others

No redirects – No need to inspect GET request payload, agnostic of HTTP_REFERER (or any other request related stuff, in its basic form)

No (changelist) view manipulation – And no template manipulations (god forbid)

Code:

(most of the imports are just for type hints and exceptions)

from typing import List, Tuple, Any

from django.contrib.admin.filters import SimpleListFilter
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList
from django.db.models.query import QuerySet
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError


class PreFilteredListFilter(SimpleListFilter):

    # Either set this or override .get_default_value()
    default_value = None

    no_filter_value = 'all'
    no_filter_name = _("All")

    # Human-readable title which will be displayed in the
    # right admin sidebar just above the filter options.
    title = None

    # Parameter for the filter that will be used in the URL query.
    parameter_name = None

    def get_default_value(self):
        if self.default_value is not None:
            return self.default_value
        raise NotImplementedError(
            'Either the .default_value attribute needs to be set or '
            'the .get_default_value() method must be overridden to '
            'return a URL query argument for parameter_name.'
        )

    def get_lookups(self) -> List[Tuple[Any, str]]:
        """
        Returns a list of tuples. The first element in each
        tuple is the coded value for the option that will
        appear in the URL query. The second element is the
        human-readable name for the option that will appear
        in the right sidebar.
        """
        raise NotImplementedError(
            'The .get_lookups() method must be overridden to '
            'return a list of tuples (value, verbose value).'
        )

    # Overriding parent class:
    def lookups(self, request, model_admin) -> List[Tuple[Any, str]]:
        return [(self.no_filter_value, self.no_filter_name)] + self.get_lookups()

    # Overriding parent class:
    def queryset(self, request, queryset: QuerySet) -> QuerySet:
        """
        Returns the filtered queryset based on the value
        provided in the query string and retrievable via
        `self.value()`.
        """
        if self.value() is None:
            return self.get_default_queryset(queryset)
        if self.value() == self.no_filter_value:
            return queryset.all()
        return self.get_filtered_queryset(queryset)

    def get_default_queryset(self, queryset: QuerySet) -> QuerySet:
        return queryset.filter(**{self.parameter_name: self.get_default_value()})

    def get_filtered_queryset(self, queryset: QuerySet) -> QuerySet:
        try:
            return queryset.filter(**self.used_parameters)
        except (ValueError, ValidationError) as e:
            # Fields may raise a ValueError or ValidationError when converting
            # the parameters to the correct type.
            raise IncorrectLookupParameters(e)

    # Overriding parent class:
    def choices(self, changelist: ChangeList):
        """
        Overridden to prevent the default "All".
        """
        value = self.value() or force_str(self.get_default_value())
        for lookup, title in self.lookup_choices:
            yield {
                'selected': value == force_str(lookup),
                'query_string': changelist.get_query_string({self.parameter_name: lookup}),
                'display': title,
            }

Full usage example:

from django.contrib import admin
from .models import SomeModelWithStatus


class StatusFilter(PreFilteredListFilter):
    default_value = SomeModelWithStatus.Status.FOO
    title = _('Status')
    parameter_name = 'status'

    def get_lookups(self):
        return SomeModelWithStatus.Status.choices


@admin.register(SomeModelWithStatus)
class SomeModelAdmin(admin.ModelAdmin):
    list_filter = (StatusFilter, )

Hope this helps somebody; feedback always appreciated.


基于Django类的视图(TemplateView)中的URL参数和逻辑

问题:基于Django类的视图(TemplateView)中的URL参数和逻辑

我不清楚在Django 1.5中如何最好地访问基于类的视图中的URL参数。

考虑以下:

视图:

from django.views.generic.base import TemplateView


class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        return context

URLCONF:

from .views import Yearly


urlpatterns = patterns('',
    url(
        regex=r'^(?P<year>\d+)/$',
        view=Yearly.as_view(),
        name='yearly-view'
    ),
)

我想year在我的视图中访问参数,因此可以执行以下逻辑:

month_names = [
    "January", "February", "March", "April", 
    "May", "June", "July", "August", 
    "September", "October", "November", "December"
]

for month, month_name in enumerate(month_names, start=1):
    is_current = False
    if year == current_year and month == current_month:
        is_current = True
        months.append({
            'month': month,
            'name': month_name,
            'is_current': is_current
        })

例如,如何最好地访问CBV中被子类化的url参数,TemplateView理想情况下应将逻辑放置在哪里?在某种方法上?

It is unclear to me how it is best to access URL-parameters in class-based-views in Django 1.5.

Consider the following:

View:

from django.views.generic.base import TemplateView


class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        return context

URLCONF:

from .views import Yearly


urlpatterns = patterns('',
    url(
        regex=r'^(?P<year>\d+)/$',
        view=Yearly.as_view(),
        name='yearly-view'
    ),
)

I want to access the year parameter in my view, so I can do logic like:

month_names = [
    "January", "February", "March", "April", 
    "May", "June", "July", "August", 
    "September", "October", "November", "December"
]

for month, month_name in enumerate(month_names, start=1):
    is_current = False
    if year == current_year and month == current_month:
        is_current = True
        months.append({
            'month': month,
            'name': month_name,
            'is_current': is_current
        })

How would one best access the url parameter in CBVs like the above that is subclassed of TemplateView and where should one ideally place the logic like this, eg. in a method?


回答 0

要在基于类的视图中访问url参数,请使用self.args或,self.kwargs这样您就可以通过self.kwargs['year']

To access the url parameters in class based views, use self.args or self.kwargs so you would access it by doing self.kwargs['year']


回答 1

如果您传递这样的URL参数:

http://<my_url>/?order_by=created

您可以使用self.request.GET(在self.args或中都未提供)在基于类的视图中访问它self.kwargs

class MyClassBasedView(ObjectList):
    ...
    def get_queryset(self):
        order_by = self.request.GET.get('order_by') or '-created'
        qs = super(MyClassBasedView, self).get_queryset()
        return qs.order_by(order_by)

In case you pass URL parameter like this:

http://<my_url>/?order_by=created

You can access it in class based view by using self.request.GET (its not presented in self.args nor in self.kwargs):

class MyClassBasedView(ObjectList):
    ...
    def get_queryset(self):
        order_by = self.request.GET.get('order_by') or '-created'
        qs = super(MyClassBasedView, self).get_queryset()
        return qs.order_by(order_by)

回答 2

我找到了这个优雅的解决方案,并且针对django 1.5或更高版本,如此处所述

Django基于通用类的视图现在自动在上下文中包含一个视图变量。此变量指向您的视图对象。

在您的views.py中:

from django.views.generic.base import TemplateView    

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"
    # Not here 
    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    # dispatch is called when the class instance loads
    def dispatch(self, request, *args, **kwargs):
        self.year = kwargs.get('year', "any_default")

    # other code

    # needed to have an HttpResponse
    return super(Yearly, self).dispatch(request, *args, **kwargs)

在这个问题中找到了调度解决方案。
由于视图已经在Template上下文中传递,因此您实际上不必担心它。在模板文件Annual.html中,可以通过以下方式简单地访问这些视图属性:

{{ view.year }}
{{ view.current_year }}
{{ view.current_month }}

您可以保持urlconf不变

值得一提的是,在模板上下文中获取信息会覆盖get_context_data(),因此某种程度上破坏了django的动作bean流。

I found this elegant solution, and for django 1.5 or higher, as pointed out here:

Django’s generic class based views now automatically include a view variable in the context. This variable points at your view object.

In your views.py:

from django.views.generic.base import TemplateView    

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"
    # Not here 
    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    # dispatch is called when the class instance loads
    def dispatch(self, request, *args, **kwargs):
        self.year = kwargs.get('year', "any_default")

    # other code

    # needed to have an HttpResponse
    return super(Yearly, self).dispatch(request, *args, **kwargs)

The dispatch solution found in this question.
As the view is already passed within Template context, you don’t really need to worry about it. In your template file yearly.html, it is possible to access those view attributes simply by:

{{ view.year }}
{{ view.current_year }}
{{ view.current_month }}

You can keep your urlconf as it is.

It’s worth mentioning that getting information into your template’s context overwrites the get_context_data(), so it is somehow breaking the django’s action bean flow.


回答 3

到目前为止,尽管我只使用ListView而不是TemplateView进行尝试,但我只能从get_queryset方法中访问这些url参数。我将使用url参数在对象实例上创建一个属性,然后在get_context_data中使用该属性来填充上下文:

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_queryset(self):
        self.year = self.kwargs['year']
        queryset = super(Yearly, self).get_queryset()
        return queryset

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        context['year'] = self.year
        return context

So far I’ve only been able to access these url parameters from within the get_queryset method, although I’ve only tried it with a ListView not a TemplateView. I’ll use the url param to create an attribute on the object instance, then use that attribute in get_context_data to populate the context:

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_queryset(self):
        self.year = self.kwargs['year']
        queryset = super(Yearly, self).get_queryset()
        return queryset

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        context['year'] = self.year
        return context

回答 4

仅仅使用Python装饰器使它变得可理解的怎么样:

class Yearly(TemplateView):

    @property
    def year(self):
       return self.kwargs['year']

How about just use Python decorators to make this intelligible:

class Yearly(TemplateView):

    @property
    def year(self):
       return self.kwargs['year']

从一个应用程序到Django中另一个应用程序的外键

问题:从一个应用程序到Django中另一个应用程序的外键

我想知道是否有可能在Django的models.py文件中定义外键,该外键引用了另一个应用程序中的表?

换句话说,我有两个应用程序,分别称为cf和profile,在cf / models.py中,我拥有(以及其他功能):

class Movie(models.Model):
    title = models.CharField(max_length=255)

并希望在profile / models.py中拥有:

class MovieProperty(models.Model):
    movie = models.ForeignKey(Movie)

但是我无法正常工作。我试过了:

    movie = models.ForeignKey(cf.Movie)

并且我曾尝试在models.py的开头导入cf.Movie,但我总是会遇到错误,例如:

NameError: name 'User' is not defined

我是通过尝试以这种方式将两个应用程序绑定在一起来违反规则,还是只是语法错误?

I’m wondering if it’s possible to define a foreign key in a models.py file in Django that is a reference to a table in another app?

In other words, I have two apps, called cf and profiles, and in cf/models.py I have (amongst other things):

class Movie(models.Model):
    title = models.CharField(max_length=255)

and in profiles/models.py I want to have:

class MovieProperty(models.Model):
    movie = models.ForeignKey(Movie)

But I can’t get it to work. I’ve tried:

    movie = models.ForeignKey(cf.Movie)

and I’ve tried importing cf.Movie at the beginning of models.py, but I always get errors, such as:

NameError: name 'User' is not defined

Am I breaking the rules by trying to tie two apps together in this way, or have I just got the syntax wrong?


回答 0

根据文档,您的第二次尝试应该可行:

要引用在另一个应用程序中定义的模型,必须改为显式指定应用程序标签。例如,如果上述制造商模型是在另一个称为生产的应用程序中定义的,则需要使用:

class Car(models.Model):
    manufacturer = models.ForeignKey('production.Manufacturer')

您是否尝试过将其用引号引起来?

According to the docs, your second attempt should work:

To refer to models defined in another application, you must instead explicitly specify the application label. For example, if the Manufacturer model above is defined in another application called production, you’d need to use:

class Car(models.Model):
    manufacturer = models.ForeignKey('production.Manufacturer')

Have you tried putting it into quotes?


回答 1

也可以通过类本身:

from django.db import models
from production import models as production_models

class Car(models.Model):
    manufacturer = models.ForeignKey(production_models.Manufacturer)

It is also possible to pass the class itself:

from django.db import models
from production import models as production_models

class Car(models.Model):
    manufacturer = models.ForeignKey(production_models.Manufacturer)

回答 2

好的-我知道了。您可以做到,只需使用正确的import语法即可。正确的语法是:

from prototype.cf.models import Movie

我的错误是没有指定该.models行的一部分。天哪!

OK – I’ve figured it out. You can do it, you just have to use the right import syntax. The correct syntax is:

from prototype.cf.models import Movie

My mistake was not specifying the .models part of that line. D’oh!


如何将Django QueryDict更改为Python Dict?

问题:如何将Django QueryDict更改为Python Dict?

假设我有以下QueryDict:

<QueryDict: {u'num': [0], u'var1': [u'value1', u'value2'], u'var2': [u'8']}>

我想要一本这样的字典,例如:

{'num': [0], 'var1':['value1', 'value2'], 'var2':['8']}

(我不在乎Unicode符号是否u继续存在。)

如果我queryDict.dict()按照django网站的建议进行操作,则会丢失属于的额外值var1,例如:

{'num': [0], 'var1':['value2'], 'var2':['8']}

我正在考虑这样做:

myDict = {}
for key in queryDict.iterkeys():
    myDict[key] = queryDict.getlist(key)

有没有更好的办法?

Let’s pretend I have the following QueryDict:

<QueryDict: {u'num': [0], u'var1': [u'value1', u'value2'], u'var2': [u'8']}>

I’d like to have a dictionary out of this, eg:

{'num': [0], 'var1':['value1', 'value2'], 'var2':['8']}

(I don’t care if the unicode symbol u stays or goes.)

If I do queryDict.dict(), as suggested by the django site, I lose the extra values belonging to var1, eg:

{'num': [0], 'var1':['value2'], 'var2':['8']}

I was thinking of doing this:

myDict = {}
for key in queryDict.iterkeys():
    myDict[key] = queryDict.getlist(key)

Is there a better way?


回答 0

这应该工作: myDict = dict(queryDict.iterlists())

This should work: myDict = dict(queryDict.iterlists())


回答 1


回答 2

这就是我最终使用的:

def qdict_to_dict(qdict):
    """Convert a Django QueryDict to a Python dict.

    Single-value fields are put in directly, and for multi-value fields, a list
    of all values is stored at the field's key.

    """
    return {k: v[0] if len(v) == 1 else v for k, v in qdict.lists()}

根据我的用法,这似乎为您提供了一个列表,您可以将其发送回例如表单构造函数。

编辑:也许这不是最好的方法。看来,如果您QueryDict出于某种疯狂的原因例如要写入文件,这是可行QueryDict.urlencode()的方法。要重建QueryDict您,只需要做QueryDict(urlencoded_data)

This is what I’ve ended up using:

def qdict_to_dict(qdict):
    """Convert a Django QueryDict to a Python dict.

    Single-value fields are put in directly, and for multi-value fields, a list
    of all values is stored at the field's key.

    """
    return {k: v[0] if len(v) == 1 else v for k, v in qdict.lists()}

From my usage this seems to get you a list you can send back to e.g. a form constructor.

EDIT: maybe this isn’t the best method. It seems if you want to e.g. write QueryDict to a file for whatever crazy reason, QueryDict.urlencode() is the way to go. To reconstruct the QueryDict you simply do QueryDict(urlencoded_data).


回答 3

from django.utils import six 
post_dict = dict(six.iterlists(request.POST))
from django.utils import six 
post_dict = dict(six.iterlists(request.POST))

回答 4

如果您不希望这些值作为数组,则可以执行以下操作:

# request = <QueryDict: {u'key': [u'123ABC']}>
dict(zip(request.GET.keys(), request.GET.values()))
{u'key': u"123ABC" }

# Only work for single item lists
# request = <QueryDict: {u'key': [u'123ABC',U 'CDEF']}>
dict(zip(request.GET.keys(), request.GET.values()))
{u'key': u"CDEF" } 

zip是一种功能强大的工具,请在此处详细了解它http://docs.python.org/2/library/functions.html#zip

If you do not want the values as Arrays you can do the following:

# request = <QueryDict: {u'key': [u'123ABC']}>
dict(zip(request.GET.keys(), request.GET.values()))
{u'key': u"123ABC" }

# Only work for single item lists
# request = <QueryDict: {u'key': [u'123ABC',U 'CDEF']}>
dict(zip(request.GET.keys(), request.GET.values()))
{u'key': u"CDEF" } 

zip is a powerful tool read more about it here http://docs.python.org/2/library/functions.html#zip


回答 5

我遇到了类似的问题,想将表单中的任意值保存为序列化值。

我的回答避免了显式地迭代字典内容: dict(querydict.iterlists())

为了检索起原始作用的类似于字典的值,可使用反函数QueryDict.setlist()来填充新QueryDict值。在这种情况下,我认为显式迭代是不可避免的。

我的辅助函数如下所示:

from django.http import QueryDict

def querydict_dict(querydict):
    """
    Converts a Django QueryDict value to a regular dictionary, preserving multiple items.
    """
    return dict(querydict.iterlists())

def dict_querydict(dict_):
    """
    Converts a value created by querydict_dict back into a Django QueryDict value.
    """
    q = QueryDict("", mutable=True)
    for k, v in dict_.iteritems():
        q.setlist(k, v)
    q._mutable = False
    return q

I ran into a similar problem, wanting to save arbitrary values from a form as serialized values.

My answer avoids explicitly iterating the dictionary contents: dict(querydict.iterlists())

In order to retrieve a dictionary-like value that functions as the original, an inverse function uses QueryDict.setlist() to populate a new QueryDict value. In this case, I don’t think the explicit iteration is avoidable.

My helper functions look like this:

from django.http import QueryDict

def querydict_dict(querydict):
    """
    Converts a Django QueryDict value to a regular dictionary, preserving multiple items.
    """
    return dict(querydict.iterlists())

def dict_querydict(dict_):
    """
    Converts a value created by querydict_dict back into a Django QueryDict value.
    """
    q = QueryDict("", mutable=True)
    for k, v in dict_.iteritems():
        q.setlist(k, v)
    q._mutable = False
    return q

回答 6

更新:

myDict = dict(queryDict._iterlists())

请注意:_的iterlists方法中的下划线queryDict。Django版本:1.5.1

Update:

myDict = dict(queryDict._iterlists())

Please Note : underscore _ in iterlists method of queryDict. Django version :1.5.1


回答 7

dict(request.POST) 返回带有数组包装值的怪异python字典。

{'start': ['2017-01-14T21:00'], 'stop': ['2017-01-14T22:00'], 'email': ['sandeep@sakethtech.com']}

作为{x:request.POST.get(x) for x in request.POST.keys()}返回预期输出的位置。

{'start': '2017-01-14T21:00', 'stop': '2017-01-14T22:00', 'email': 'sandeep@sakethtech.com'}

dict(request.POST) returns a weird python dictionary with array wrapped values.

{'start': ['2017-01-14T21:00'], 'stop': ['2017-01-14T22:00'], 'email': ['sandeep@sakethtech.com']}

where as {x:request.POST.get(x) for x in request.POST.keys()} returns expected output.

{'start': '2017-01-14T21:00', 'stop': '2017-01-14T22:00', 'email': 'sandeep@sakethtech.com'}

回答 8

只需添加

queryDict=dict(request.GET) 要么 queryDict=dict(QueryDict)

在您的视图中,数据将以python Dict的形式保存在querDict中。

just simply add

queryDict=dict(request.GET) or queryDict=dict(QueryDict)

In your view and data will be saved in querDict as python Dict.


回答 9

这就是我解决该问题的方法:

dict_ = {k: q.getlist(k) if len(q.getlist(k))>1 else v for k, v in q.items()}

This is how I solved that problem:

dict_ = {k: q.getlist(k) if len(q.getlist(k))>1 else v for k, v in q.items()}

回答 10

和我一样,您可能更熟悉Dict()python中的方法。但是,QueryDict()它也是易于使用的对象。例如,也许您想从中获取值request.GET QueryDict()

您可以这样操作:request.GET.__getitem__(<key>)

QueryDict()文档:https : //docs.djangoproject.com/en/2.0/ref/request-response/#django.http.QueryDict

Like me, you probably are more familiar with Dict() methods in python. However, the QueryDict() is also an easy object to use. For example, perhaps you wanted to get the value from the request.GET QueryDict().

You can do this like so: request.GET.__getitem__(<key>).

QueryDict() documentation: https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.QueryDict


回答 11

随着Django 2.2很少有干净的解决方案:

  1. QueryDict.dict()这是最简单的方法,但是会破坏QueryDict列表作为值,例如:
from django.http.request import QueryDict, MultiValueDict

query_dict = QueryDict('', mutable=True)
query_dict.update(MultiValueDict({'a': ['one', 'two']}))
query_dict.update({'b': 'three'})

for key, value in query_dict.dict().items():  # ---> query_dict.dict()
    print(key, value)

将输出

a two  # <--- missed 'one'
b three
  1. dict(QueryDict) 更好,因为它将使列表正确字典:
from django.http.request import QueryDict, MultiValueDict

query_dict = QueryDict('', mutable=True)
query_dict.update(MultiValueDict({'a': ['one', 'two']}))
query_dict.update({'b': 'three'})

for key, value in dict(query_dict).items():  # ---> dict(query_dict)
    print(key, value)

将输出

a ['one', 'two']
b ['three']

哪个是正确的。

With Django 2.2 there are few clean solutions:

  1. QueryDict.dict() is simplest but it will broke QueryDict with lists as values, e.g:
from django.http.request import QueryDict, MultiValueDict

query_dict = QueryDict('', mutable=True)
query_dict.update(MultiValueDict({'a': ['one', 'two']}))
query_dict.update({'b': 'three'})

for key, value in query_dict.dict().items():  # ---> query_dict.dict()
    print(key, value)

will output

a two  # <--- missed 'one'
b three
  1. dict(QueryDict) is better because it will make correct dictionary of lists:
from django.http.request import QueryDict, MultiValueDict

query_dict = QueryDict('', mutable=True)
query_dict.update(MultiValueDict({'a': ['one', 'two']}))
query_dict.update({'b': 'three'})

for key, value in dict(query_dict).items():  # ---> dict(query_dict)
    print(key, value)

will output

a ['one', 'two']
b ['three']

which is correct.


回答 12

我尝试了两者dict(request.POST)request.POST.dict()并意识到,如果您的list值中'var1':['value1', 'value2']嵌套了一些值request.POST,则late(request.POST.dict())仅使我可以访问嵌套列表中的最后一项,而前一个(dict(request.POST))则允许我访问嵌套列表中的所有项目。

我希望这可以帮助别人。

I tried out both dict(request.POST) and request.POST.dict() and realised that if you have list values for example 'var1':['value1', 'value2'] nested in your request.POST, the later(request.POST.dict()) only gave me access to the last item in a nested list while the former(dict(request.POST)) allowed me to access all items in a nested list.

I hope this helps someone.


最好的Django搜索应用是什么?[关闭]

问题:最好的Django搜索应用是什么?[关闭]

我正在建立一个需要搜索功能的Django项目,直到有一个 django.contrib.search,我必须选择一个搜索应用程序。那么,哪个最好?“最好”是指…

  • 易于安装/设置
  • 具有Django或至少Python友好的API
  • 可以执行相当复杂的搜索

这是我听说过的一些应用程序,如果您知道其中任何一个,请建议其他应用程序:

我还想避免使用第三方搜索引擎(例如Google SiteSearch),因为我想索引的某些数据仅适用于网站成员,不应公开。

I’m building a Django project that needs search functionality, and until there’s a django.contrib.search, I have to choose a search app. So, which is the best? By “best” I mean…

  • easy to install / set up
  • has a Django- or at least Python-friendly API
  • can perform reasonably complex searches

Here are some apps I’ve heard of, please suggest others if you know of any:

I’d also like to avoid using a third-party search engine (like Google SiteSearch), because some of the data I’d like to index is for site members only and should not be public.


回答 0

查阅Haystack Search-一个基于模型的新模型,该抽象层目前支持XapianSolrWhoosh。看起来它得到了很好的支持和记录。

Check out Haystack Search – a new model based search abstraction layer that currently supports Xapian, Solr and Whoosh. Looks like it’s well supported and documented.


回答 1

贾斯汀,我先尝试djangosearch:Jacob Kaplan-Moss(Django的首席开发人员)正在研究它。

潜在危险:

  • 主页警告该API可能并不完全稳定

潜在的好处:

  • “长远的目标是要实现这一目标django.contrib.search。”

Justin, I’d try djangosearch first: Jacob Kaplan-Moss (Django’s lead developer) is working on it.

Potential hazards:

  • The home page warns the API might not be entirely stable

Potential benefits:

  • “The long term goal is for this to become django.contrib.search.”

回答 2

我正在寻找同一件事,就像其他许多人一样。希望很快会添加django.contrib.search

同时,这是我发现的内容:

对我来说,大多数看起来很复杂,坦率地说,实施起来有些艰巨。我很想了解您对这些的看法。

I am searching for the same thing, as are a lot of other people. Let’s hope that django.contrib.search will be added soon.

In the meantime, this is what I found:

To me, most look quite complicated and, frankly, a little daunting to implement. I’d be interested to learn what you think of these.


回答 3

djangosearch的google代码页表明它不再处于积极开发中,并建议haystacksolango

The google code page for djangosearch indicates that it is no longer under active development, and suggests haystack or solango.


回答 4

我建议使用Sphinx进行全文搜索和聚合,而django-sphinx足以用于生产环境。我们发现Sphinx是索引和搜索文档的资源最少,最快的方式,而django-sphinx是sphinx客户端之上的一个很好的包装器。

如果您想显示有多少个带有特定标签或由某个作者(或两者兼有)匹配的文档,则按聚合分组特别好。在内存中,属性更新也很方便,特别是对于立即删除已删除的文章。

I’d recommend Sphinx for full-text search and aggregation, and django-sphinx is good enough for production use. We found that Sphinx was the least resource-intensive and fastest way to index and search our documents and that django-sphinx was a nice wrapper on top of the sphinx client.

The group by aggregation is particularly nice, if for example you want to display how many documents with a certain tag or by a certain author (or both) matched a search. In memory attribute updates were convenient too, especially for removing deleted articles immediately.


回答 5

谢谢加思。我曾经见过djangosearch想成为正式的Django搜索,但是由于无法找到任何文档,我还是犹豫使用它!幸运的是,在Subversion中有一个自述文件,我以前从未见过,它使API看起来很酷:

# set up the model
class Event(models.Model):
    title = models.CharField(max_length=255)
    date = models.DateField()
    is_outdoors = models.BooleanField()

    index = djangosearch.ModelIndex(text=['title'], 
                                    additional=['date', 'is_outdoors'])

# run a search
results = Event.index.search("django conference")

Thanks Garth. I had seen that djangosearch wanted to become the official Django search, but I was hesitant to use it because I couldn’t find any documentation! Luckily, there’s a README in subversion that I hadn’t seen before, and it makes the API look very cool:

# set up the model
class Event(models.Model):
    title = models.CharField(max_length=255)
    date = models.DateField()
    is_outdoors = models.BooleanField()

    index = djangosearch.ModelIndex(text=['title'], 
                                    additional=['date', 'is_outdoors'])

# run a search
results = Event.index.search("django conference")

回答 6

我只需要一个非常快速的解决方案,对于内部应用程序来说就算是小菜一碟。

我很快找到了文章“ 将搜索添加到Django”,这对我来说非常出色!

显然,它缺乏像Haystack这样的真实项目的速度,可伸缩性和功能,但是此项目更易于设置,除关键字AND-search外,我真的不需要其他任何东西。

I just needed a very quick solution that was no-fuss for an internal app.

I found the article Adding search to Django in a snap, and that worked splendid for me!

Obviously it lacks the speed, scalability and features of the real projects like Haystack, but this one is easier to set up, and I don’t really need anything else than keyword AND-search.


回答 7

您可能需要考虑让Yahoo借助其构建自己的搜索服务(BOSS)来完成所有艰苦的工作。这是一篇很棒的博客文章,它将引导您完成整个过程:http : //www.peterkrantz.com/2008/yahoo-search-in-django/

You might want to consider letting Yahoo do all the hard work with their Build your own Search Service (BOSS). Here is a great blog post that walks you through the process: http://www.peterkrantz.com/2008/yahoo-search-in-django/


回答 8

好像这里的每个人都错过了django-xappy

在对Django的所有现有搜索插件进行了快速评估之后,我发现这是最灵活,最易于使用的插件。在几个地方的边缘都很粗糙,但这仍然是在Django项目中使用Xapian搜索引擎功能的最佳方法。

It looks like everyone here missed django-xappy

After quick evaluation of all existing search addons for Django, I found this one as most flexible and easiest to use. It’s rough on the edges in few places, but it’s still the best way to use power of Xapian search engine inside Django projects.


回答 9

您可能想看一下Django Solr搜索(又名“ Solango”),它附带了一些不错的文档来帮助您入门…

You might want to look at Django Solr search (aka “Solango”) which comes with some nice documentation to get you started…


回答 10

如果您要对大量数据建立索引或期望获得高流量,建议您使用一些外部搜索引擎,例如Solr。这样,您将保持无共享方式,并能够独立扩展站点组件。

If you have large amount of data to be indexed or you expect high traffic, I’d suggest using some external search engine, like Solr. This way, you’ll keep shared-nothing approach and be able to scale your site components independently.


回答 11

我想我将不得不向贾比安大喊大叫。

坚如磐石…只需拉下源代码分布并查看内部即可。一流的代码,评论不多。

它仍然是一个年轻的软件项目,但是我认为django社区应该将它的精力放在这个项目上。

I think I am going to have to give a shout out to Djapian.

It is rock-solid…just pull down a source distribution and peek inside. Top notch code, not very many comments tho..

It’s still a young software project, but I think the django community should throw it’s weight behind this one.


回答 12

谢谢乔,

我们决定使用Tsearch2和自定义的postgres适配器。Tsearch2不需要额外的进程来运行,这很方便,因为我们是在内存有限的WebFaction主机上进行的……它尚未完全完成,但似乎是一个很好的解决方案……

Thanks Joe,

We decided to go with Tsearch2 and a custom postgres adaptor. Tsearch2 does not need an extra process to run, which was convenient since we are on a WebFaction hosting with limited memory… It’s not completely done yet, but seems to be a good solution…


回答 13

我发现Djoosh依靠纯Python外部搜索引擎Whoosh与我的“ Python”大脑很好地配合。

I found Djoosh which relies on the pure-python external search engine Whoosh to work well with my ‘Python’ brain.


回答 14

如果您愿意使用第三方搜索引擎,我可以推荐Yahoo BOSSdjango-bosssearch

Yahoo BOSS是一项付费服务​​,但是它可以节省您在服务器上设置和维护其他搜索软件的费用。

If you are willing to use a 3rd party search engine I can recommend Yahoo BOSS and django-bosssearch.

Yahoo BOSS is a paid service, but it saves you setting up and maintaining other search software on your server.


如何在PyCharm终端中激活virtualenv?

问题:如何在PyCharm终端中激活virtualenv?

我已经设置了PyCharm,创建了我的virtualenv(通过virtual env命令,或者直接在PyCharm中),并将那个环境激活为我的解释器。一切正常。

但是,如果我使用“工具,打开终端”打开终端,则提供的shell提示使用虚拟环境。我仍然必须source ~/envs/someenv/bin/activate在该终端内使用才能激活它。

另一种方法是在外壳中激活环境,然后从该环境运行PyCharm。这是“可行的”但很丑陋,这意味着如果我从PyCharm切换环境或项目,我会遇到重大问题:我现在使用的是完全错误的环境。

还有其他更简便的方法来使“工具,打开终端”自动激活虚拟环境吗?

I’ve set up PyCharm, created my virtualenv (either through the virtual env command, or directly in PyCharm) and activated that environment as my Interpreter. Everything is working just fine.

However, if I open a terminal using “Tools, Open Terminal”, the shell prompt supplied is not using the virtual env; I still have to use source ~/envs/someenv/bin/activate within that Terminal to activate it.

Another method is to activate the environment in a shell, and run PyCharm from that environment. This is “workable” but pretty ugly, and means I have major problems if I switch environments or projects from PyCharm: I’m now using the totally-wrong environment.

Is there some other, much-easier way to have “Tools, Open Terminal” automatically activate the virtual environment?


回答 0

编辑:

根据https://www.jetbrains.com/pycharm/whatsnew/#v2016-3-venv-in-terminal的介绍,PyCharm 2016.3(于2016年11月发布)具有开箱即用的virutalenv支持

bash,zsh,fish和Windows cmd支持自动virtualenv。您可以在“设置”(“首选项”)|“自定义”中自定义外壳首选项。工具| 终奌站。


旧方法:

.pycharmrc在主文件夹中创建一个包含以下内容的文件

source ~/.bashrc
source ~/pycharmvenv/bin/activate

使用您的virtualenv路径作为最后一个参数。

然后将Shell Preferences-> Project Settings-> Shell path设置为

/bin/bash --rcfile ~/.pycharmrc

Edit:

According to https://www.jetbrains.com/pycharm/whatsnew/#v2016-3-venv-in-terminal, PyCharm 2016.3 (released Nov 2016) has virutalenv support for terminals out of the box

Auto virtualenv is supported for bash, zsh, fish, and Windows cmd. You can customize your shell preference in Settings (Preferences) | Tools | Terminal.


Old Method:

Create a file .pycharmrc in your home folder with the following contents

source ~/.bashrc
source ~/pycharmvenv/bin/activate

Using your virtualenv path as the last parameter.

Then set the shell Preferences->Project Settings->Shell path to

/bin/bash --rcfile ~/.pycharmrc

回答 1

更新:

设置(首选项)|首选项中的首选项 工具| 终端是全球性的。
如果为每个项目使用venv,请记住使用当前路径变量和默认venv名称:

"cmd.exe" /k ""%CD%\venv\Scripts\activate"" 

对于Windows用户:在虚拟环境中使用PyCharm时,可以使用该/K参数cmd.exe自动设置虚拟环境。

PyCharm 3或4: ,,Settings 和添加。TerminalDefault shell/K <path-to-your-activate.bat>

PyCharm 5: ,SettingsToolsTerminal并添加/K <path-to-your-activate.bat>Shell path

PyCharm 2016.1或2016.2: ,SettingsToolsTerminal并添加""/K <path-to-your-activate.bat>""Shell path,并添加(介意引号)。还要在cmd.exe周围加上引号,导致:

"cmd.exe" /k ""C:\mypath\my-venv\Scripts\activate.bat""

Update:

The preferences in Settings (Preferences) | Tools | Terminal are global.
If you use a venv for each project, remember to use current path variable and a default venv name:

"cmd.exe" /k ""%CD%\venv\Scripts\activate"" 

For Windows users: when using PyCharm with a virtual environment, you can use the /K parameter to cmd.exe to set the virtual environment automatically.

PyCharm 3 or 4: Settings, Terminal, Default shell and add /K <path-to-your-activate.bat>.

PyCharm 5: Settings, Tools, Terminal, and add /K <path-to-your-activate.bat> to Shell path.

PyCharm 2016.1 or 2016.2: Settings, Tools, Terminal, and add ""/K <path-to-your-activate.bat>"" to Shell path and add (mind the quotes). Also add quotes around cmd.exe, resulting in:

"cmd.exe" /k ""C:\mypath\my-venv\Scripts\activate.bat""


回答 2

对于Windows用户,在Windows下使用PyCharm和虚拟环境时,可以使用/ k参数cmd.exe来自动设置虚拟环境。

转到“设置”,“终端”,“默认外壳”并添加/K <path-to-your-activate.bat>

我没有对早期回复发表评论的声誉,因此请发布此更正版本。这确实节省了很多时间。

更新:

注意:Pycharm现在直接支持虚拟环境,对我来说似乎很好用-因此不再需要我的解决方法。

For Windows users when using PyCharm and a virtual environment under Windows, you can use the /k parameter to cmd.exe to set the virtual environment automatically.

Go to Settings, Terminal, Default shell and add /K <path-to-your-activate.bat>.

I don’t have the reputation to comment on the earlier response so posting this corrected version. This really saves a LOT of time.

Update:

Note: Pycharm now supports virtual environments directly and it seems to work well for me – so my workaround not needed anymore.


回答 3

根据彼得的回答和实验,我提出了一个很好的“一般解决方案”,可以解决以下问题:

  • 恢复登录外壳程序的行为。PyCharm通常运行登录外壳程序,但是–rcfile阻止了这种情况的发生。脚本仍然使用–rcfile,但是尝试模拟登录shell的INVOCATION行为。
  • 无需为每个环境创建rcfile
  • 如果您更改环境,则无需更新项目设置。

将此脚本放入bin目录中的某个位置。例如〜/ bin / pycharmactivate

if [ -r "/etc/profile" ] ; then . /etc/profile ; fi
if [ -r "~/.bash_profile" ] ; then
    . ~/.bash_profile
elif [ -r "~/.bash_login" ] ; then
    . ~/.bash_login
elif [ -r "~/.profile" ] ; then
    . ~/.profile
fi
ACTIVATERC=`cat .idea/workspace.xml | perl -n -e 'print "\$1/bin/activate" if m:option name="SDK_HOME" value="\\\$USER_HOME\\\$(.*)/bin/python":'`
if [ -n "$ACTIVATERC" ] ; then . "$HOME/$ACTIVATERC" ; else echo "Could not find virtualenv from PyCharm" ; fi

然后将PyCharm的Shell路径设置为:

/bin/bash --rcfile ~/bin/pycharmactivate

Based on answers from Peter and experimentation, I’ve come up with a good “general solution”, which solves the following:

  • Restores the behaviour of a login shell. PyCharm normally runs a login shell, but –rcfile stopped this happening. Script still uses –rcfile, but attempts to emulate the INVOCATION behaviour of a login shell.
  • Removes the need to create an rcfile for each environment
  • Removes the need to update the project settings if you change the environment.

Drop this script into a bin directory somewhere. E.g. ~/bin/pycharmactivate

if [ -r "/etc/profile" ] ; then . /etc/profile ; fi
if [ -r "~/.bash_profile" ] ; then
    . ~/.bash_profile
elif [ -r "~/.bash_login" ] ; then
    . ~/.bash_login
elif [ -r "~/.profile" ] ; then
    . ~/.profile
fi
ACTIVATERC=`cat .idea/workspace.xml | perl -n -e 'print "\$1/bin/activate" if m:option name="SDK_HOME" value="\\\$USER_HOME\\\$(.*)/bin/python":'`
if [ -n "$ACTIVATERC" ] ; then . "$HOME/$ACTIVATERC" ; else echo "Could not find virtualenv from PyCharm" ; fi

Then set PyCharm’s Shell path to:

/bin/bash --rcfile ~/bin/pycharmactivate

回答 4

PyCharm 4现在在IDE中集成了virtualenvs。选择项目解释器时,可以创建,添加或选择一个virtualenv。他们添加了一个在配置的项目解释器中运行的“ Python控制台”。

更多信息在这里。

PyCharm 4 now has virtualenvs integrated in the IDE. When selecting your project interpreter, you can create, add, or select a virtualenv. They’ve added a “Python Console” that runs in the configured project interpreter.

More info here.


回答 5

谢谢克里斯,您的脚本适用于某些项目,但不是我的机器上的全部。这是我编写的脚本,希望任何人都觉得它有用。

#Stored in ~/.pycharmrc 

ACTIVATERC=$(python -c 'import re
import os
from glob import glob

try:
  #sets Current Working Directory to _the_projects .idea folder
  os.chdir(os.getcwd()+"/.idea") 

  #gets every file in the cwd and sets _the_projects iml file
  for file in glob("*"): 
    if re.match("(.*).iml", file):
      project_iml_file = file

  #gets _the_virtual_env for _the_project
  for line in open(project_iml_file):
    env_name = re.findall("~/(.*)\" jdkType", line.strip())
    # created or changed a virtual_env after project creation? this will be true
    if env_name:
      print env_name[0] + "/bin/activate"
      break

    inherited = re.findall("type=\"inheritedJdk\"", line.strip())
    # set a virtual_env during project creation? this will be true
    if inherited:
      break

  # find _the_virtual_env in misc.xml
  if inherited:
    for line in open("misc.xml").readlines():
      env_at_project_creation = re.findall("\~/(.*)\" project-jdk", line.strip())
      if env_at_project_creation:
        print env_at_project_creation[0] + "/bin/activate"
        break
finally:
  pass
')

if [ "$ACTIVATERC" ] ; then . "$HOME/$ACTIVATERC" ; fi

Thanks Chris, your script worked for some projects but not all on my machine. Here is a script that I wrote and I hope anyone finds it useful.

#Stored in ~/.pycharmrc 

ACTIVATERC=$(python -c 'import re
import os
from glob import glob

try:
  #sets Current Working Directory to _the_projects .idea folder
  os.chdir(os.getcwd()+"/.idea") 

  #gets every file in the cwd and sets _the_projects iml file
  for file in glob("*"): 
    if re.match("(.*).iml", file):
      project_iml_file = file

  #gets _the_virtual_env for _the_project
  for line in open(project_iml_file):
    env_name = re.findall("~/(.*)\" jdkType", line.strip())
    # created or changed a virtual_env after project creation? this will be true
    if env_name:
      print env_name[0] + "/bin/activate"
      break

    inherited = re.findall("type=\"inheritedJdk\"", line.strip())
    # set a virtual_env during project creation? this will be true
    if inherited:
      break

  # find _the_virtual_env in misc.xml
  if inherited:
    for line in open("misc.xml").readlines():
      env_at_project_creation = re.findall("\~/(.*)\" project-jdk", line.strip())
      if env_at_project_creation:
        print env_at_project_creation[0] + "/bin/activate"
        break
finally:
  pass
')

if [ "$ACTIVATERC" ] ; then . "$HOME/$ACTIVATERC" ; fi

回答 6

我已经查看了上面所有的答案,但是没有一个对我来说足够优雅。在Pycharm 2017.1.3(在我的计算机中)中,最简单的方法是打开Settings->Tools->Terminal并检查Shell integrationActivate virtualenv选项。

图片

I have viewed all of the answers above but none of them is elegant enough for me. In Pycharm 2017.1.3(in my computer), the easiest way is to open Settings->Tools->Terminal and check Shell integration and Activate virtualenv options.

image


回答 7

如果您使用的是Windows版本,则非常简单。如果您已经拥有虚拟环境,则只需导航至其文件夹,然后在文件夹activate.bat内找到即可Scripts。复制它的完整路径,并将其粘贴到pycharm的终端中,然后按Enter完成。

如果您需要创建新的虚拟环境:

转到“文件”>“设置”,然后搜索project interpreter,打开,单击齿轮按钮并在所需的位置创建环境,然后按照第一段进行操作。

齿轮!

If You are using windows version it is quite easy. If you already have the virtual environment just navigate to its folder, find activate.bat inside Scripts folder. copy it’s full path and paste it in pycharm’s terminal then press Enter and you’re done!

If you need to create new virtual environment :

Go to files > settings then search for project interpreter, open it, click on gear button and create the environment wherever you want and then follow first paragraph.

The Gear!


回答 8

在Mac上,它是PyCharm => Preferences … => Tools => Terminal => Activate virtualenv,默认情况下应启用。

On Mac it’s PyCharm => Preferences… => Tools => Terminal => Activate virtualenv, which should be enabled by default.


回答 9

我刚刚在主目录中添加了一个名为pycharmactivate的脚本。设置PyCharm(4.0.1)文件>设置>工具>终端> / bin / bash –rcfile〜/ pycharmactivate的Shell路径的值。如果您有不同的项目和virtualenv目录/名称,也许不是最好的解决方案,但是它对我有用。该脚本包含以下3行,并假定您的virtualenv与项目目录具有相同的名称。

source ~/.bashrc
projectdir=${PWD##*/}
source ~/.virtualenvs/$projectdir/bin/activate

I just added a script named pycharmactivate to my home directory. Set value of PyCharm (4.0.1) File > Settings > Tools > Terminal > Shell path to /bin/bash –rcfile ~/pycharmactivate. Maybe not the best solution incase you have different project and virtualenv directories/names but it works for me. This script contains the following 3 lines and assumes your virtualenv has the same name as your project dir.

source ~/.bashrc
projectdir=${PWD##*/}
source ~/.virtualenvs/$projectdir/bin/activate

回答 10

跟进Peter的回答,这里是.pycharmrc文件的Mac版本:

source /etc/profile
source ~/.bash_profile
source  <venv_dir>/bin/activate

母鸡

Following up on Peter’s answer, here the Mac version of the .pycharmrc file:

source /etc/profile
source ~/.bash_profile
source  <venv_dir>/bin/activate

Hen


回答 11

我有一个可以在Windows 7计算机上使用的解决方案。

我相信PyCharm的终端是它运行的结果,它将cmd.exe加载Windows PATH变量,并使用在其中首先找到的Python版本PATH。要编辑此变量,请右键单击我的电脑 -> 属性 -> 高级系统设置 -> 高级选项卡-> 环境变量…按钮。在“ 系统变量”部分中,选择并编辑PATH变量。

这是我编辑PATH 之前的相关部分:

C:\ Python27 \;
C:\ Python27 \ Lib \ site-packages \ pip \;
C:\ Python27 \ Scripts;
C:\ Python27 \ Lib \ site-packages \ django \ bin;

…并且编辑PATH(现在仅3行):

C:[project_path] \ virtualenv-Py2.7_Dj1.7 \ Lib \ site-packages \ pip;
C:[project_path] \ virtualenvs \ virtualenv-Py2.7_Dj1.7 \ Scripts;
C:[project_path] \ virtualenvs \ virtualenv-Py2.7_Dj1.7 \ Lib \ site-packages \ django \ bin;

要测试这一点,请打开一个新的 Windows终端(开始 ->键入cmd并点击Enter),看看它是否正在使用您的虚拟环境。如果可行,请重新启动PyCharm,然后在PyCharm的终端中对其进行测试。

I have a solution that worked on my Windows 7 machine.

I believe PyCharm’s terminal is a result of it running cmd.exe, which will load the Windows PATH variable, and use the version of Python that it finds first within that PATH. To edit this variable, right click My Computer –> Properties –> Advanced System Settings –> Advanced tab –> Environment Variables… button. Within the System variables section, select and edit the PATH variable.

Here is the relevant part of my PATH before editing:

C:\Python27\;
C:\Python27\Lib\site-packages\pip\;
C:\Python27\Scripts;
C:\Python27\Lib\site-packages\django\bin;

…and after editing PATH (only 3 lines now):

C:[project_path]\virtualenv-Py2.7_Dj1.7\Lib\site-packages\pip;
C:[project_path]\virtualenvs\virtualenv-Py2.7_Dj1.7\Scripts;
C:[project_path]\virtualenvs\virtualenv-Py2.7_Dj1.7\Lib\site-packages\django\bin;

To test this, open a new windows terminal (Start –> type in cmd and hit Enter) and see if it’s using your virtual environment. If that works, restart PyCharm and then test it out in PyCharm’s terminal.


回答 12

这就是我在做什么:在源代码文件夹中创建一个activate_env.bat(在Linux中为Windows,也许是.sh)文件:

/env_yourenvlocate/scripts/activate.bat

和另一个文件deactivate_env.bat:

/env_yourenvlocate/scripts/deactivate.bat

每次打开终端窗口时,只需执行bat文件来激活/停用virtualenv,您将停留在源代码路径中,而无需更改路径。

E:\Projects\django_study\src>active_env.bat

E:\Projects\django_study\src>../env_django_study/scripts/activate.bat
(env_django_study) E:\Projects\django_study\src>



(env_django_study) E:\Projects\django_study\src>deactive_env.bat

(env_django_study)E:\Projects\django_study\src>../env_django_study/scripts/deactivate.bat
E:\Projects\django_study\src>

this is what i am doing: create a activate_env.bat(windows,maybe .sh in linux) file in the source code folde:

/env_yourenvlocate/scripts/activate.bat

and another file deactivate_env.bat:

/env_yourenvlocate/scripts/deactivate.bat

everytime open the terminal window, just execute the bat file to activate/deactivate the virtualenv, you will stay in source code path, no need to change path to and back.

E:\Projects\django_study\src>active_env.bat

E:\Projects\django_study\src>../env_django_study/scripts/activate.bat
(env_django_study) E:\Projects\django_study\src>



(env_django_study) E:\Projects\django_study\src>deactive_env.bat

(env_django_study)E:\Projects\django_study\src>../env_django_study/scripts/deactivate.bat
E:\Projects\django_study\src>

回答 13

如果您的Pycharm 2016.1.4v及更高版本应使用 "default path" /K "<path-to-your-activate.bat>" 不要忘记引号

If your Pycharm 2016.1.4v and higher you should use "default path" /K "<path-to-your-activate.bat>" don’t forget quotes


回答 14

如果您已将项目移动到另一个目录,则可以通过“设置”对话框设置新路径。然后,您需要在“编辑配置”对话框中设置此项目解释器。

在此处输入图片说明

在此处输入图片说明

If you have moved your project to another directory, you can set the new path via Settings dialog. And then you need to set this Project Interpreter in the Edit Configuration dialog.

enter image description here

enter image description here


回答 15

另一种选择是使用virtualenvwrapper来管理您的虚拟环境。看来,一旦virtualenvwrapper 脚本被激活,pycharm就可以使用它,然后workon从pycharm控制台可以使用简单的命令,并为您提供可用的虚拟环境:

kevin@debian:~/Development/django-tutorial$ workon
django-tutorial
FlaskHF
SQLAlchemy
themarkdownapp
kevin@debian:~/Development/django-tutorial$ workon django-tutorial
(django-tutorial)kevin@debian:~/Development/django-tutorial$ 

Another alternative is to use virtualenvwrapper to manage your virtual environments. It appears that once the virtualenvwrapper script is activated, pycharm can use that and then the simple workon command will be available from the pycharm console and present you with the available virtual environments:

kevin@debian:~/Development/django-tutorial$ workon
django-tutorial
FlaskHF
SQLAlchemy
themarkdownapp
kevin@debian:~/Development/django-tutorial$ workon django-tutorial
(django-tutorial)kevin@debian:~/Development/django-tutorial$ 

回答 16

该方法应该在每个项目的任意虚拟环境下都可以使用,并且由于使用创建的钩子,因此不会对您的环境做任何假设。

你写:

  • 调用钩子的全局脚本
  • 每个PyCharm项目的挂钩脚本(不是必需的)

鉴于当前最新的PyCharm(社区2016.1)不允许每个项目的终端设置都调用项目特定挂钩的脚本开头。这是我的~/.pycharmrc

if [ -r ".pycharm/term-activate" ]; then
   echo "Terminal activation hook detected."
   echo "Loading Bash profile..."
   source ~/.bash_profile
   echo "Activating terminal hook..."
   source ".pycharm/term-activate"
   source activate $PYCHARM_VENV
fi

如果您使用的不是Bash,.bash_profile则应调用自己的等效项。

现在,将PyCharm设置为“工具->终端->外壳路径”以调用此脚本,例如: /bin/bash --rcfile ~/.pycharmrc

最后,对于每个PyCharm项目,您都需要激活特定的虚拟环境,请在PyCharm项目root内创建一个文件.pycharm/term-activate。这是您的钩子,它将仅为您的PyCharm项目定义所需的虚拟环境的名称:

export PYCHARM_VENV=<your-virtual-env-name>

当然,您可以使用在您的特定PyCharm项目的终端环境中发现有用的任何东西来扩展自己的功能。

This method should work with arbitrary virtual environments per project and it doesn’t make assumptions on your environment as it is using hooks you create.

You write:

  • A global script that invokes the hook
  • A hook script per PyCharm project (not mandatory)

Given that the current latest PyCharm (Community 2016.1) does not allow for Terminal settings per project start with the script that invokes the project specific hook. This is my ~/.pycharmrc:

if [ -r ".pycharm/term-activate" ]; then
   echo "Terminal activation hook detected."
   echo "Loading Bash profile..."
   source ~/.bash_profile
   echo "Activating terminal hook..."
   source ".pycharm/term-activate"
   source activate $PYCHARM_VENV
fi

If you are using something other than Bash, invoke your own .bash_profile equivalent should you wish to.

Now set your PyCharm “Tools -> Terminal -> Shell Path” to invoke this script, e.g.: /bin/bash --rcfile ~/.pycharmrc

Finally, for every PyCharm project you need a specific virtual environment activated, create a file within the PyCharm project root .pycharm/term-activate. This is your hook and it will simply define the name of the desired virtual environment for your PyCharm project:

export PYCHARM_VENV=<your-virtual-env-name>

You can of course extend your hooks with anything you find useful in the terminal environment of your particular PyCharm project.


回答 17

对于Windows上的conda虚拟环境,请确保未命名您的批处理文件,activate.bat因为这将导致与conda activate命令冲突,从而导致对该批处理文件的递归调用。

下面的Shell路径对我有用:

"cmd.exe" /k ""C:\FullPathToYourProject\activate-env.bat""

并在activate-env.bat文件中:

call activate myenvname

For conda virtual environments on Windows, make sure your batch file is NOT named activate.bat as this will cause a conflict with the conda activate command, resulting in a recursive calling of the batch file.

What works for me is the following Shell path:

"cmd.exe" /k ""C:\FullPathToYourProject\activate-env.bat""

And in the activate-env.bat file:

call activate myenvname

回答 18

我希望为每个项目提供一个单独的虚拟环境,并且不太在乎是否需要其他文件来简化此工作。您只需执行一次即可用于所有项目的解决方案,然后将以下内容添加到您的.bashrc或中.bash_profile

if [ -d "./venv" ]; then
    source ./venv/bin/activate
fi

这将检查是否存在打开终端的虚拟环境,是否激活了终端(当然可以使用其他相对路径)。PyCharm的终端设置可以保留为默认设置。

I wanted a separate virtual environment for each project, and didn’t care much for having additional files to facilitate this. A solution which you only need to do once and works for all projects is then adding the following to your .bashrc or .bash_profile:

if [ -d "./venv" ]; then
    source ./venv/bin/activate
fi

This checks if there is a virtual environment where the terminal is being opened, and if so activates it (and of course other relative paths could be used). PyCharm’s terminal settings can be left as their default.


回答 19

PyCharm 4.5.4

使用以下内容在主文件夹中创建文件.pycharmrc

source ~/.bashrc
source ~/pycharmvenv/bin/activate

使用您的virtualenv路径作为最后一个参数。

然后将Shell Preferences-> Project Settings-> Shell path设置为

/bin/bash --rcfile ~/.pycharmrc

我不知道为什么,但这对我不起作用。PyCharm打印错误。

cmd.exe /K "<path-to-your-activate.bat>" 它可以工作,但是即使没有必要,它也会为每个项目创建相同的virtualenv。

收据有效!但是字符串/env_yourenvlocate/scripts/activate.bat必须包含引号,像这样"Full_path_to_your_env_locate\scripts\activate.bat"

禁用virtualenv非常容易-在终端中输入’deactivate’

(virt_env) D:\Projects\src>deactivate
D:\Projects\src>

PyCharm 4.5.4

Create a file .pycharmrc in your home folder with the following contents

source ~/.bashrc
source ~/pycharmvenv/bin/activate

Using your virtualenv path as the last parameter.

Then set the shell Preferences->Project Settings->Shell path to

/bin/bash --rcfile ~/.pycharmrc

I don’t why, but it doesn’t work for me. PyCharm prints an error.

cmd.exe /K "<path-to-your-activate.bat>" It works, but it creates the same virtualenv for each project, and even if this is not necessary.

This receipt is working! But the string /env_yourenvlocate/scripts/activate.bat must contain quotes, like this "Full_path_to_your_env_locate\scripts\activate.bat"!

Deactivate the virtualenv is very easy – type in the terminal ‘deactivate’

(virt_env) D:\Projects\src>deactivate
D:\Projects\src>

回答 20

WSL解决方案(Windows上的Ubuntu)

如果您使用的是WSL(在Windows上为Ubuntu),则还可以在pycharm中将bash作为终端打开,并激活linux virtualenv。

使用.pycharmrc类似于Peter Gibson答案中所述的文件;将.pycharmrc文件添加到您的主目录,其中包含以下内容:

source ~/.bashrc
source ~/path_to_virtualenv/bin/activate

在Pycharm 文件>设置>工具>终端中,添加以下“外壳路径”:

"C:/Windows/system32/bash.exe" -c "bash --rcfile ~/.pycharmrc"


项目特定的virtualenv

您的virtualenv的路径.pycharmrc不必是绝对的。您可以通过在项目目录中设置相对路径来设置特定于项目的virtualenv。我的virtualenv始终位于项目目录下的“ venv”文件夹中,因此.pycharmrc文件如下所示:

来源〜/ .bashrc
源〜/ pycharmvenv / bin / activate#绝对路径
源./venv/bin/activate#相对路径


奖励:自动打开ssh隧道以将virtualenv连接为项目解释器

将以下内容添加到您的.pycharmrc文件中:

if [ $(ps -aux | grep -c 'ssh') -lt 2 ]; then
    sudo service ssh start 
fi

这将检查ssh隧道是否已经打开,否则打开一个。在 Pycharm的“文件”->“设置”->“项目”->“项目解释器”中,添加具有以下配置的新远程解释器:

+ -------------------------- + ---------------------- ----------- + ------- + ---- +
| 名称:<口译员名称> | | |
| 选择| “ SSH凭证” | | |
| 主持人:127.0.0.1 | 端口:| 22 |
| 用户:| <Linux用户名> | | |
| 验证类型:| “密码” | | |
| 密码:<Linux密码> | | |
| Python解释器路径:<Linux到您的virtualenv的路径> | | |
| Python帮助程序路径:| <自动设置> | | |
+ -------------------------- + ---------------------- ----------- + ------- + ---- +

现在,当您打开项目时,bash会自动在virtualenv中启动,打开ssh隧道,并且pycharm将virtualenv连接为远程解释器。

警告:Windows中的最新更新将在启动时自动启动SshBroker和SshProxy服务。这些阻止了从Linux到Windows的ssh隧道。您可以在“任务管理器”->“服务”中停止这些服务,然后所有内容将再次运行。

Solution for WSL (Ubuntu on Windows)

If you’re using WSL (Ubuntu on Windows), you can also open bash as terminal in pycharm and activate a linux virtualenv.

Use a .pycharmrc file like described in Peter Gibson’s answer; Add the .pycharmrc file to your home directory with following content:

source ~/.bashrc
source ~/path_to_virtualenv/bin/activate

In Pycharm File > Settings > Tools > Terminal add the following ‘Shell path’:

"C:/Windows/system32/bash.exe" -c "bash --rcfile ~/.pycharmrc"


Project specific virtualenv

The path to your virtualenv in .pycharmrc does not have to be absolute. You can set a project specific virtualenv by setting a relative path from your project directory. My virtualenv is always located in a ‘venv’ folder under my project directory, so my .pycharmrc file looks like this:

source ~/.bashrc
source ~/pycharmvenv/bin/activate #absolute path
source ./venv/bin/activate #relative path


BONUS: automatically open ssh tunnel to connect virtualenv as project interpreter

Add the following to your .pycharmrc file:

if [ $(ps -aux | grep -c 'ssh') -lt 2 ]; then
    sudo service ssh start 
fi

This checks if a ssh tunnel is already opened, and opens one otherwise. In File -> Settings -> Project -> Project Interpreter in Pycharm, add a new remote interpreter with following configuration:

+--------------------------+---------------------------------+-------+----+
| Name:                    | <Interpreter name>              |       |    |
| Select                   | 'SSH Credentials'               |       |    |
| Host:                    | 127.0.0.1                       | Port: | 22 |
| User:                    | <Linux username>                |       |    |
| Auth type:               | 'Password'                      |       |    |
| Password:                | <Linux password>                |       |    |
| Python interpreter path: | <Linux path to your virtualenv> |       |    |
| Python helpers path:     | <Set automatically>             |       |    |
+--------------------------+---------------------------------+-------+----+

Now when you open your project, your bash automatically starts in your virtualenv, opens a ssh tunnel, and pycharm connects the virtualenv as remote interpreter.

warning: the last update in Windows automatically starts a SshBroker and SshProxy service on startup. These block the ssh tunnel from linux to windows. You can stop these services in Task Manager -> Services, after which everything will work again.


回答 21

输入终端>运行>调试>编辑配置时,您有一个选择 在此处输入图片说明

在此处输入图片说明

选择合适的conda环境。。同样,在创建新项目时-它要求配置该位置。

One option you have when you enter the terminal > Run > Debug > Edit Configurations enter image description here

enter image description here

select the appropriate conda environmnent.. Also when you create a new project – it asks to configure this location.


Django-如何创建文件并将其保存到模型的FileField中?

问题:Django-如何创建文件并将其保存到模型的FileField中?

这是我的模特。我想要做的是生成一个新文件,并在保存模型实例时覆盖现有文件:

class Kitten(models.Model):
    claw_size = ...
    license_file = models.FileField(blank=True, upload_to='license')

    def save(self, *args, **kwargs):
        #Generate a new license file overwriting any previous version
        #and update file path
        self.license_file = ???
        super(Request,self).save(*args, **kwargs)

我看到很多有关如何上传文件的文档。但是,如何生成文件,将其分配给模型字段并将Django存储在正确的位置呢?

Here’s my model. What I want to do is generate a new file and overwrite the existing one whenever a model instance is saved:

class Kitten(models.Model):
    claw_size = ...
    license_file = models.FileField(blank=True, upload_to='license')

    def save(self, *args, **kwargs):
        #Generate a new license file overwriting any previous version
        #and update file path
        self.license_file = ???
        super(Request,self).save(*args, **kwargs)

I see lots of documentation about how to upload a file. But how do I generate a file, assign it to a model field and have Django store it in the right place?


回答 0

您想看看Django文档中的FileField和FieldFile,尤其是FieldFile.save()

基本上,FileField在访问时声明为的字段为您提供class的实例FieldFile,该实例为您提供了几种与基础文件进行交互的方法。因此,您需要做的是:

self.license_file.save(new_name, new_contents)

new_name您要分配的文件名在哪里,并且new_contents是文件的内容。请注意,new_contents该实例必须是django.core.files.File或的一个实例django.core.files.base.ContentFile(有关详细信息,请参见给定的手册链接)。这两个选择可以归结为:

# Using File
f = open('/path/to/file')
self.license_file.save(new_name, File(f))
# Using ContentFile
self.license_file.save(new_name, ContentFile('A string with the file content'))

You want to have a look at FileField and FieldFile in the Django docs, and especially FieldFile.save().

Basically, a field declared as a FileField, when accessed, gives you an instance of class FieldFile, which gives you several methods to interact with the underlying file. So, what you need to do is:

self.license_file.save(new_name, new_contents)

where new_name is the filename you wish assigned and new_contents is the content of the file. Note that new_contents must be an instance of either django.core.files.File or django.core.files.base.ContentFile (see given links to manual for the details). The two choices boil down to:

# Using File
f = open('/path/to/file')
self.license_file.save(new_name, File(f))
# Using ContentFile
self.license_file.save(new_name, ContentFile('A string with the file content'))

回答 1

接受的答案当然是一个很好的解决方案,但是这就是我生成CSV并从视图中提供它的方法。

我认为这是值得的,因为我花了点时间来摆弄所有想要的行为(覆盖现有文件,存储到正确的位置,不创建重复的文件等)。

的Django 1.4.1

Python 2.7.3

#Model
class MonthEnd(models.Model):
    report = models.FileField(db_index=True, upload_to='not_used')

import csv
from os.path import join

#build and store the file
def write_csv():
    path = join(settings.MEDIA_ROOT, 'files', 'month_end', 'report.csv')
    f = open(path, "w+b")

    #wipe the existing content
    f.truncate()

    csv_writer = csv.writer(f)
    csv_writer.writerow(('col1'))

    for num in range(3):
        csv_writer.writerow((num, ))

    month_end_file = MonthEnd()
    month_end_file.report.name = path
    month_end_file.save()

from my_app.models import MonthEnd

#serve it up as a download
def get_report(request):
    month_end = MonthEnd.objects.get(file_criteria=criteria)

    response = HttpResponse(month_end.report, content_type='text/plain')
    response['Content-Disposition'] = 'attachment; filename=report.csv'

    return response

Accepted answer is certainly a good solution, but here is the way I went about generating a CSV and serving it from a view.

Thought it was worth while putting this here as it took me a little bit of fiddling to get all the desirable behaviour (overwrite existing file, storing to the right spot, not creating duplicate files etc).

Django 1.4.1

Python 2.7.3

#Model
class MonthEnd(models.Model):
    report = models.FileField(db_index=True, upload_to='not_used')

import csv
from os.path import join

#build and store the file
def write_csv():
    path = join(settings.MEDIA_ROOT, 'files', 'month_end', 'report.csv')
    f = open(path, "w+b")

    #wipe the existing content
    f.truncate()

    csv_writer = csv.writer(f)
    csv_writer.writerow(('col1'))

    for num in range(3):
        csv_writer.writerow((num, ))

    month_end_file = MonthEnd()
    month_end_file.report.name = path
    month_end_file.save()

from my_app.models import MonthEnd

#serve it up as a download
def get_report(request):
    month_end = MonthEnd.objects.get(file_criteria=criteria)

    response = HttpResponse(month_end.report, content_type='text/plain')
    response['Content-Disposition'] = 'attachment; filename=report.csv'

    return response

回答 2

close()在文件保存过程中,如果有异常情况,最好使用上下文管理器或进行调用。如果您的存储后端关闭等情况,可能会发生。

任何覆盖行为都应在存储后端中配置。例如S3Boto3Storage有一个设置AWS_S3_FILE_OVERWRITE。如果您正在使用FileSystemStorage,则可以编写自定义mixin

如果您希望发生任何自定义的副作用(例如最近更新的时间戳记),则可能还需要调用模型的save方法而不是FileField的save方法。如果是这种情况,您还可以将文件的name属性设置为文件的名称-相对于MEDIA_ROOT。它默认为文件的完整路径,如果不设置它,可能会引起问题-请参见File .__ init __()File.name

这是一个示例实例,其中self是模型实例,其中my_file是FileField / ImageFile,它调用save()整个模型实例,而不仅仅是FileField:

import os
from django.core.files import File

with open(filepath, 'rb') as fi:
    self.my_file = File(fi, name=os.path.basename(fi.name))
    self.save()

It’s good practice to use a context manager or call close() in case of exceptions during the file saving process. Could happen if your storage backend is down, etc.

Any overwrite behavior should be configured in your storage backend. For example S3Boto3Storage has a setting AWS_S3_FILE_OVERWRITE. If you’re using FileSystemStorage you can write a custom mixin.

You might also want to call the model’s save method instead of the FileField’s save method if you want any custom side-effects to happen, like last-updated timestamps. If that’s the case, you can also set the name attribute of the file to the name of the file – which is relative to MEDIA_ROOT. It defaults to the full path of the file which can cause problems if you don’t set it – see File.__init__() and File.name.

Here’s an example where self is the model instance where my_file is the FileField / ImageFile, calling save() on the whole model instance instead of just FileField:

import os
from django.core.files import File

with open(filepath, 'rb') as fi:
    self.my_file = File(fi, name=os.path.basename(fi.name))
    self.save()

在Django Rest Framework的响应中包括中介(通过模型)

问题:在Django Rest Framework的响应中包括中介(通过模型)

我有一个关于通过模型处理m2m /及其在django rest框架中的演示的问题。让我们举一个经典的例子:

models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

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

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

在获取Member的实例时,我成功地接收了成员的所有字段以及它的组-但是,我仅获得组的详细信息,而没有来自Membership模型的其他详细信息。

换句话说,我希望收到:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

注意join_date

我已经尝试了很多解决方案,当然包括有关它的Django Rest-Framework官方页面,而且似乎没有人给出正确的简单答案-我需要怎么做才能包括这些额外的字段?我发现使用django-tastypie更加简单明了,但是还有其他一些问题,并且更喜欢休息框架。

I have a question about dealing with m2m / through models and their presentation in django rest framework. Let’s take a classic example:

models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

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

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

When GETing an instance of Member, I successfully receive all of the member’s fields and also its groups – however I only get the groups’ details, without extra details that comes from the Membership model.

In other words I expect to receive:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

Note the join_date.

I have tried oh so many solutions, including of course Django Rest-Framework official page about it and no one seems to give a proper plain answer about it – what do I need to do to include these extra fields? I found it more straight-forward with django-tastypie but had some other problems and prefer rest-framework.


回答 0

怎么样…..

在您的MemberSerializer上,在其上定义一个字段,如下所示:

groups = MembershipSerializer(source='membership_set', many=True)

然后在会员序列化器上可以创建以下代码:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

这具有创建序列化值,组的总体效果,该序列化组具有所需的成员身份作为源,然后使用自定义序列化程序提取要显示的位。

编辑:由@bryanph评论,在DRF 3.0中serializers.field被重命名为serializers.ReadOnlyField,因此应显示为:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

适用于任何现代实施

How about…..

On your MemberSerializer, define a field on it like:

groups = MembershipSerializer(source='membership_set', many=True)

and then on your membership serializer you can create this:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

That has the overall effect of creating a serialized value, groups, that has as its source the membership you want, and then it uses a custom serializer to pull out the bits you want to display.

EDIT: as commented by @bryanph, serializers.field was renamed to serializers.ReadOnlyField in DRF 3.0, so this should read:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

for any modern implementations


回答 1

我遇到了这个问题,我的解决方案(使用DRF 3.6)是在对象上使用SerializerMethodField并显式查询Membership表,如下所示:

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

这将返回组键的字典列表,其中每个字典都从MembershipSerializer序列化。为了使其可写,您可以在MemberSerializer内定义自己的create / update方法,在其中迭代输入数据并显式创建或更新Membership模型实例。

I was facing this problem and my solution (using DRF 3.6) was to use SerializerMethodField on the object and explicitly query the Membership table like so:

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

This will return a list of dicts for the groups key where each dict is serialized from the MembershipSerializer. To make it writable, you can define your own create/update method inside the MemberSerializer where you iterate over the input data and explicitly create or update Membership model instances.


回答 2

注意:作为一名软件工程师,我喜欢使用体系结构,并且我在分层开发方法方面做过深入的工作,因此我将就层级回答它。

据我了解问题,这是解决方案models.py

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

从技术上讲,您必须将请求传递给DataAccessLayer,该请求将从数据访问层返回过滤的对象,但是由于我必须以快速的方式回答问题,所以我在业务逻辑层中调整了代码!

NOTE: As a Software Engineer, I love to use Architectures and I have deeply worked on Layered Approach for Development so I am gonna be Answering it with Respect to Tiers.

As i understood the Issue, Here’s the Solution models.py

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

You would technically, have to pass the Request to DataAccessLayer which would return the Filtered Objects from Data Access Layer but as I have to Answer the Question in a Fast Manner so i adjusted the Code in Business Logic Layer!


我应该如何在Django中编写有关Forms的测试?

问题:我应该如何在Django中编写有关Forms的测试?

在编写测试时,我想在Django中模拟对视图的请求。这主要是测试表格。这是一个简单的测试请求的片段:

from django.tests import TestCase

class MyTests(TestCase):
    def test_forms(self):
        response = self.client.post("/my/form/", {'something':'something'})
        self.assertEqual(response.status_code, 200) # we get our page back with an error

无论是否存在表单错误,页面始终返回200的响应。如何检查我的表格是否失败以及特定字段(soemthing)是否存在错误?

I’d like to simulate requests to my views in Django when I’m writing tests. This is mainly to test the forms. Here’s a snippet of a simple test request:

from django.tests import TestCase

class MyTests(TestCase):
    def test_forms(self):
        response = self.client.post("/my/form/", {'something':'something'})
        self.assertEqual(response.status_code, 200) # we get our page back with an error

The page always returns a response of 200 whether there’s an form error or not. How can I check that my Form failed and that the particular field (soemthing) had an error?


回答 0

我认为,如果您只想测试表单,则应该只测试表单,而不是测试呈现表单的视图。举例说明:

from django.test import TestCase
from myapp.forms import MyForm

class MyTests(TestCase):
    def test_forms(self):
        form_data = {'something': 'something'}
        form = MyForm(data=form_data)
        self.assertTrue(form.is_valid())
        ... # other tests relating forms, for example checking the form data

I think if you just want to test the form, then you should just test the form and not the view where the form is rendered. Example to get an idea:

from django.test import TestCase
from myapp.forms import MyForm

class MyTests(TestCase):
    def test_forms(self):
        form_data = {'something': 'something'}
        form = MyForm(data=form_data)
        self.assertTrue(form.is_valid())
        ... # other tests relating forms, for example checking the form data

回答 1

https://docs.djangoproject.com/zh-CN/stable/topics/testing/tools/#django.test.SimpleTestCase.assertFormError

from django.tests import TestCase

class MyTests(TestCase):
    def test_forms(self):
        response = self.client.post("/my/form/", {'something':'something'})
        self.assertFormError(response, 'form', 'something', 'This field is required.')

其中“ form”是表单的上下文变量名称,“ something”是字段名称,而“此字段是必需的”。是预期验证错误的确切文本。

https://docs.djangoproject.com/en/stable/topics/testing/tools/#django.test.SimpleTestCase.assertFormError

from django.tests import TestCase

class MyTests(TestCase):
    def test_forms(self):
        response = self.client.post("/my/form/", {'something':'something'})
        self.assertFormError(response, 'form', 'something', 'This field is required.')

Where “form” is the context variable name for your form, “something” is the field name, and “This field is required.” is the exact text of the expected validation error.


回答 2

2011年的原始答案是

self.assertContains(response, "Invalid message here", 1, 200)

但是我现在看到(2018)有大量适用的断言可用

  • assertRaisesMessage
  • assertFieldOutput
  • assertFormError
  • assertFormsetError

随便你吧。

The original 2011 answer was

self.assertContains(response, "Invalid message here", 1, 200)

But I see now (2018) there is a whole crowd of applicable asserts available:

  • assertRaisesMessage
  • assertFieldOutput
  • assertFormError
  • assertFormsetError

Take your pick.