标签归档:Django

Cron和virtualenv

问题:Cron和virtualenv

我正在尝试从cron运行Django管理命令。我正在使用virtualenv使我的项目沙盒化。

我在这里和其他地方都看到了一些示例,这些示例显示了从virtualenv内部运行的管理命令,例如:

0 3 * * * source /home/user/project/env/bin/activate && /home/user/project/manage.py command arg

但是,即使syslog在任务应该启动时显示一个条目,该任务也不会实际运行(脚本的日志文件为空)。如果我从外壳程序手动运行该行,它将按预期工作。

目前,我可以使命令通过cron运行的唯一方法是将这些命令分解并放在一个笨拙的bash包装器脚本中:

#!/bin/sh
source /home/user/project/env/bin/activate
cd /home/user/project/
./manage.py command arg

编辑:

ars提出了一种有效的命令组合:

0 3 * * * cd /home/user/project && /home/user/project/env/bin/python /home/user/project/manage.py command arg

至少就我而言,为virtualenv调用激活脚本没有任何作用。这是可行的,因此在演出中如此。

I am trying to run a Django management command from cron. I am using virtualenv to keep my project sandboxed.

I have seen examples here and elsewhere that show running management commands from within virtualenv’s like:

0 3 * * * source /home/user/project/env/bin/activate && /home/user/project/manage.py command arg

However, even though syslog shows an entry when the task should have started, this task never actually runs (the log file for the script is empty). If I run the line manually from the shell, it works as expected.

The only way I can currently get the command to run via cron, is to break the commands up and put them in a dumb bash wrapper script:

#!/bin/sh
source /home/user/project/env/bin/activate
cd /home/user/project/
./manage.py command arg

EDIT:

ars came up with a working combination of commands:

0 3 * * * cd /home/user/project && /home/user/project/env/bin/python /home/user/project/manage.py command arg

At least in my case, invoking the activate script for the virtualenv did nothing. This works, so on with the show.


回答 0

您应该可以通过python在虚拟环境中使用来执行此操作:

/home/my/virtual/bin/python /home/my/project/manage.py command arg

编辑:如果您的django项目不在PYTHONPATH中,那么您需要切换到正确的目录:

cd /home/my/project && /home/my/virtual/bin/python ...

您也可以尝试从cron记录故障:

cd /home/my/project && /home/my/virtual/bin/python /home/my/project/manage.py > /tmp/cronlog.txt 2>&1

另一件事是尝试在manage.py脚本的最顶部进行相同的更改:

#!/home/my/virtual/bin/python

You should be able to do this by using the python in your virtual environment:

/home/my/virtual/bin/python /home/my/project/manage.py command arg

EDIT: If your django project isn’t in the PYTHONPATH, then you’ll need to switch to the right directory:

cd /home/my/project && /home/my/virtual/bin/python ...

You can also try to log the failure from cron:

cd /home/my/project && /home/my/virtual/bin/python /home/my/project/manage.py > /tmp/cronlog.txt 2>&1

Another thing to try is to make the same change in your manage.py script at the very top:

#!/home/my/virtual/bin/python

回答 1

source从cronfile 运行将无法正常运行,因为cron /bin/sh用作其默认外壳程序,该外壳程序不支持source。您需要将SHELL环境变量设置为/bin/bash

SHELL=/bin/bash
*/10 * * * * root source /path/to/virtualenv/bin/activate && /path/to/build/manage.py some_command > /dev/null

很难找到失败原因,因为/var/log/syslog它不会记录错误详细信息。最好将自己别名为root,以便通过电子邮件发送任何cron错误。只需添加自己即可/etc/aliases运行sendmail -bi

此处提供更多信息:http : //codeinthehole.com/archives/43-Running-django-cronjobs-within-a-virtualenv.html

上面的链接已更改为:https : //codeinthehole.com/tips/running-django-cronjobs-within-a-virtualenv/

Running source from a cronfile won’t work as cron uses /bin/sh as its default shell, which doesn’t support source. You need to set the SHELL environment variable to be /bin/bash:

SHELL=/bin/bash
*/10 * * * * root source /path/to/virtualenv/bin/activate && /path/to/build/manage.py some_command > /dev/null

It’s tricky to spot why this fails as /var/log/syslog doesn’t log the error details. Best to alias yourself to root so you get emailed with any cron errors. Simply add yourself to /etc/aliases and run sendmail -bi.

More info here: http://codeinthehole.com/archives/43-Running-django-cronjobs-within-a-virtualenv.html

the link above is changed to: https://codeinthehole.com/tips/running-django-cronjobs-within-a-virtualenv/


回答 2

不要再看了:

0 3 * * * /usr/bin/env bash -c 'cd /home/user/project && source /home/user/project/env/bin/activate && ./manage.py command arg' > /dev/null 2>&1

通用方法:

* * * * * /usr/bin/env bash -c 'YOUR_COMMAND_HERE' > /dev/null 2>&1

这样做的好处是您无需将SHELLcrontab 的变量从更改shbash

Don’t look any further:

0 3 * * * /usr/bin/env bash -c 'cd /home/user/project && source /home/user/project/env/bin/activate && ./manage.py command arg' > /dev/null 2>&1

Generic approach:

* * * * * /usr/bin/env bash -c 'YOUR_COMMAND_HERE' > /dev/null 2>&1

The beauty about this is you DO NOT need to change the SHELL variable for crontab from sh to bash


回答 3

使用virtualenv时,运行python cron作业的唯一正确方法是激活环境,然后执行环境的python来运行代码。

一种方法是activate_this在您的python脚本中使用virtualenv ,请参阅:http : //virtualenv.readthedocs.org/en/latest/userguide.html#using-virtualenv-without-bin-python

另一个解决方案是回显完整的命令,包括激活环境并将其传递到/bin/bash。考虑为您的/etc/crontab

***** root echo 'source /env/bin/activate; python /your/script' | /bin/bash

The only correct way to run python cron jobs when using a virtualenv is to activate the environment and then execute the environment’s python to run your code.

One way to do this is use virtualenv’s activate_this in your python script, see: http://virtualenv.readthedocs.org/en/latest/userguide.html#using-virtualenv-without-bin-python

Another solution is echoing the complete command including activating the environment and piping it into /bin/bash. Consider this for your /etc/crontab:

***** root echo 'source /env/bin/activate; python /your/script' | /bin/bash

回答 4

与其摆弄特定于virtualenv的shebang,不如摆PATH在crontab上。

在激活的virtualenv中,运行以下三个命令,并且python脚本应该可以正常工作:

$ echo "PATH=$PATH" > myserver.cron
$ crontab -l >> myserver.cron
$ crontab myserver.cron

现在,crontab的第一行应如下所示:

PATH=/home/me/virtualenv/bin:/usr/bin:/bin:  # [etc...]

Rather than mucking around with virtualenv-specific shebangs, just prepend PATH onto the crontab.

From an activated virtualenv, run these three commands and python scripts should just work:

$ echo "PATH=$PATH" > myserver.cron
$ crontab -l >> myserver.cron
$ crontab myserver.cron

The crontab’s first line should now look like this:

PATH=/home/me/virtualenv/bin:/usr/bin:/bin:  # [etc...]

回答 5

对我来说最好的解决方案是

  • 使用venv bin /目录中的python二进制文件
  • 设置python路径以包含venv modules目录。

man python提到使用以下命令在shell中$PYTHONPATH或在python 中修改路径sys.path

其他答案提到使用shell进行此操作的想法。在python中,将以下行添加到我的脚本中使我可以直接从cron成功运行它。

import sys
sys.path.insert(0,'/path/to/venv/lib/python3.3/site-packages');

这是交互式会话中的外观-

Python 3.3.2+ (default, Feb 28 2014, 00:52:16) 
[GCC 4.8.1] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import sys

>>> sys.path
['', '/usr/lib/python3.3', '/usr/lib/python3.3/plat-x86_64-linux-gnu', '/usr/lib/python3.3/lib-dynload']

>>> import requests
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'requests'   

>>> sys.path.insert(0,'/path/to/venv/modules/');

>>> import requests
>>>

The best solution for me was to both

  • use the python binary in the venv bin/ directory
  • set the python path to include the venv modules directory.

man python mentions modifying the path in shell at $PYTHONPATH or in python with sys.path

Other answers mention ideas for doing this using the shell. From python, adding the following lines to my script allows me to successfully run it directly from cron.

import sys
sys.path.insert(0,'/path/to/venv/lib/python3.3/site-packages');

Here’s how it looks in an interactive session —

Python 3.3.2+ (default, Feb 28 2014, 00:52:16) 
[GCC 4.8.1] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import sys

>>> sys.path
['', '/usr/lib/python3.3', '/usr/lib/python3.3/plat-x86_64-linux-gnu', '/usr/lib/python3.3/lib-dynload']

>>> import requests
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'requests'   

>>> sys.path.insert(0,'/path/to/venv/modules/');

>>> import requests
>>>

回答 6

我想添加这个内容是因为我花了一些时间解决问题,但在这里找不到cron和virtualenv中变量使用组合的答案。所以也许会帮助某人。

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DIR_SMTH="cd /smth"
VENV=". venv/bin/activate"
CMD="some_python_bin do_something"
# m h  dom mon dow   command
0 * * * * $DIR_SMTH && $VENV && $CMD -k2 some_target >> /tmp/crontest.log 2>&1

像这样配置时效果不佳

DIR_SMTH =“ cd / smth &&。venv / bin / activate”

感谢@ davidwinterbottom@ reed-sandberg@mkb提供正确的方向。在您的python需要运行一个脚本(该脚本必须从venv / bin目录运行另一个python二进制文件)之前,可接受的答案实际上可以正常工作。

I’d like to add this because I spent some time solving the issue and did not find an answer here for combination of variables usage in cron and virtualenv. So maybe it’ll help someone.

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DIR_SMTH="cd /smth"
VENV=". venv/bin/activate"
CMD="some_python_bin do_something"
# m h  dom mon dow   command
0 * * * * $DIR_SMTH && $VENV && $CMD -k2 some_target >> /tmp/crontest.log 2>&1

It did not work well when it was configured like

DIR_SMTH=”cd /smth && . venv/bin/activate”

Thanks @davidwinterbottom, @reed-sandberg and @mkb for giving the right direction. The accepted answer actually works fine until your python need to run a script which have to run another python binary from venv/bin directory.


回答 7

这是一个对我有效的解决方案。

source /root/miniconda3/etc/profile.d/conda.sh && \
conda activate <your_env> && \
python <your_application> &

我在Ubuntu 18.04.3 LTS上使用带有Conda版本4.7.12的miniconda。

我可以将以上内容放入脚本中,并通过crontab进行运行,而不会遇到任何麻烦。

This is a solution that has worked well for me.

source /root/miniconda3/etc/profile.d/conda.sh && \
conda activate <your_env> && \
python <your_application> &

I am using miniconda with Conda version 4.7.12 on a Ubuntu 18.04.3 LTS.

I am able to place the above inside a script and run it via crontab as well without any trouble.


回答 8

python脚本

from datetime import datetime                                                                                                                                                                
import boto   # check wheather its taking the virtualenv or not                                                                                                                                                                        
import sys                                                                                                                                                                                   
param1=sys.argv[1]     #Param                                                                                                                                                                                                                                                                                                                                                                    
myFile = open('appendtxt.txt', 'a')                                                                                                                                                      
myFile.write('\nAccessed on ' + param1+str(datetime.now())) 

Cron命令

 */1 * * * *  cd /Workspace/testcron/ && /Workspace/testcron/venvcron/bin/python3  /Workspace/testcron/testcronwithparam.py param  

在上面的命令中

  • * / 1 * * * * -每1分钟执行一次
  • cd / Workspace / testcron /-python脚本的路径
  • / Workspace / testcron / venvcron / bin / python3 -Virtualenv路径
  • Workspace / testcron / testcronwithparam.py-文件路径
  • 参数 -参数

python script

from datetime import datetime                                                                                                                                                                
import boto   # check wheather its taking the virtualenv or not                                                                                                                                                                        
import sys                                                                                                                                                                                   
param1=sys.argv[1]     #Param                                                                                                                                                                                                                                                                                                                                                                    
myFile = open('appendtxt.txt', 'a')                                                                                                                                                      
myFile.write('\nAccessed on ' + param1+str(datetime.now())) 

Cron command

 */1 * * * *  cd /Workspace/testcron/ && /Workspace/testcron/venvcron/bin/python3  /Workspace/testcron/testcronwithparam.py param  

In above command

  • */1 * * * * – Execute every one minte
  • cd /Workspace/testcron/ – Path of the python script
  • /Workspace/testcron/venvcron/bin/python3 – Virtualenv path
  • Workspace/testcron/testcronwithparam.py – File path
  • param – parameter

如何在Django ModelForm中过滤ForeignKey选择?

问题:如何在Django ModelForm中过滤ForeignKey选择?

说我的内容如下models.py

class Company(models.Model):
   name = ...

class Rate(models.Model):
   company = models.ForeignKey(Company)
   name = ...

class Client(models.Model):
   name = ...
   company = models.ForeignKey(Company)
   base_rate = models.ForeignKey(Rate)

即有多个Companies,每个都有一个Rates和的范围Clients。每个数据库Client都应有一个Rate从其父数据库中选择的碱基,而Company's Rates不是另一个Company's Rates

创建用于添加的表单时Client,我想删除Company选择(因为已经通过Company页面上的“添加客户端”按钮Rate选择了该选项),并且也将选择限制在此范围内Company

我该如何在Django 1.0中做到这一点?

目前,我当前的forms.py文件只是样板:

from models import *
from django.forms import ModelForm

class ClientForm(ModelForm):
    class Meta:
        model = Client

而且views.py也是基本的:

from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *

def addclient(request, company_id):
    the_company = get_object_or_404(Company, id=company_id)

    if request.POST:
        form = ClientForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(the_company.get_clients_url())
    else:
        form = ClientForm()

    return render_to_response('addclient.html', {'form': form, 'the_company':the_company})

在Django 0.96中,我可以通过在渲染模板之前进行如下操作来破解:

manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]

ForeignKey.limit_choices_to似乎很有希望,但我不知道该如何传递,the_company.id也不清楚是否可以在管理界面之外使用。

谢谢。(这似乎是一个非常基本的要求,但是如果我应该重新设计一些内容,我可以提出建议。)

Say I have the following in my models.py:

class Company(models.Model):
   name = ...

class Rate(models.Model):
   company = models.ForeignKey(Company)
   name = ...

class Client(models.Model):
   name = ...
   company = models.ForeignKey(Company)
   base_rate = models.ForeignKey(Rate)

I.e. there are multiple Companies, each having a range of Rates and Clients. Each Client should have a base Rate that is chosen from it’s parent Company's Rates, not another Company's Rates.

When creating a form for adding a Client, I would like to remove the Company choices (as that has already been selected via an “Add Client” button on the Company page) and limit the Rate choices to that Company as well.

How do I go about this in Django 1.0?

My current forms.py file is just boilerplate at the moment:

from models import *
from django.forms import ModelForm

class ClientForm(ModelForm):
    class Meta:
        model = Client

And the views.py is also basic:

from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *

def addclient(request, company_id):
    the_company = get_object_or_404(Company, id=company_id)

    if request.POST:
        form = ClientForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(the_company.get_clients_url())
    else:
        form = ClientForm()

    return render_to_response('addclient.html', {'form': form, 'the_company':the_company})

In Django 0.96 I was able to hack this in by doing something like the following before rendering the template:

manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]

ForeignKey.limit_choices_to seems promising but I don’t know how to pass in the_company.id and I’m not clear if that will work outside the Admin interface anyway.

Thanks. (This seems like a pretty basic request but if I should redesign something I’m open to suggestions.)


回答 0

ForeignKey由django.forms.ModelChoiceField表示,这是一个ChoiceField,其选择是模型QuerySet。请参见ModelChoiceField的参考。

因此,为字段的queryset属性提供一个QuerySet 。取决于表单的构建方式。如果构建显式表单,则将具有直接命名的字段。

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

如果采用默认的ModelForm对象, form.fields["rate"].queryset = ...

这是在视图中明确完成的。不得乱动。

ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet. See the reference for ModelChoiceField.

So, provide a QuerySet to the field’s queryset attribute. Depends on how your form is built. If you build an explicit form, you’ll have fields named directly.

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

If you take the default ModelForm object, form.fields["rate"].queryset = ...

This is done explicitly in the view. No hacking around.


回答 1

除了S.Lott的回答以及在注释中提到的成为Guru之外,还可以通过覆盖该ModelForm.__init__函数来添加queryset过滤器。(这可以很容易地应用于常规表单)它可以帮助重用并保持视图功能整洁。

class ClientForm(forms.ModelForm):
    def __init__(self,company,*args,**kwargs):
        super (ClientForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['rate'].queryset = Rate.objects.filter(company=company)
        self.fields['client'].queryset = Client.objects.filter(company=company)

    class Meta:
        model = Client

def addclient(request, company_id):
        the_company = get_object_or_404(Company, id=company_id)

        if request.POST:
            form = ClientForm(the_company,request.POST)  #<-- Note the extra arg
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(the_company.get_clients_url())
        else:
            form = ClientForm(the_company)

        return render_to_response('addclient.html', 
                                  {'form': form, 'the_company':the_company})

如果您在许多模型上都需要通用过滤器(通常我会声明一个抽象Form类),这对于重用很有用。例如

class UberClientForm(ClientForm):
    class Meta:
        model = UberClient

def view(request):
    ...
    form = UberClientForm(company)
    ...

#or even extend the existing custom init
class PITAClient(ClientForm):
    def __init__(company, *args, **args):
        super (PITAClient,self ).__init__(company,*args,**kwargs)
        self.fields['support_staff'].queryset = User.objects.exclude(user='michael')

除此之外,我只是在重申Django博客材料,其中有很多不错的材料。

In addition to S.Lott’s answer and as becomingGuru mentioned in comments, its possible to add the queryset filters by overriding the ModelForm.__init__ function. (This could easily apply to regular forms) it can help with reuse and keeps the view function tidy.

class ClientForm(forms.ModelForm):
    def __init__(self,company,*args,**kwargs):
        super (ClientForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['rate'].queryset = Rate.objects.filter(company=company)
        self.fields['client'].queryset = Client.objects.filter(company=company)

    class Meta:
        model = Client

def addclient(request, company_id):
        the_company = get_object_or_404(Company, id=company_id)

        if request.POST:
            form = ClientForm(the_company,request.POST)  #<-- Note the extra arg
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(the_company.get_clients_url())
        else:
            form = ClientForm(the_company)

        return render_to_response('addclient.html', 
                                  {'form': form, 'the_company':the_company})

This can be useful for reuse say if you have common filters needed on many models (normally I declare an abstract Form class). E.g.

class UberClientForm(ClientForm):
    class Meta:
        model = UberClient

def view(request):
    ...
    form = UberClientForm(company)
    ...

#or even extend the existing custom init
class PITAClient(ClientForm):
    def __init__(company, *args, **args):
        super (PITAClient,self ).__init__(company,*args,**kwargs)
        self.fields['support_staff'].queryset = User.objects.exclude(user='michael')

Other than that I’m just restating Django blog material of which there are many good ones out there.


回答 2

这很简单,并且可以在Django 1.4中使用:

class ClientAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ClientAdminForm, self).__init__(*args, **kwargs)
        # access object through self.instance...
        self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)

class ClientAdmin(admin.ModelAdmin):
    form = ClientAdminForm
    ....

您不需要在表单类中指定它,但是可以直接在ModelAdmin中指定它,因为Django已经在ModelAdmin中包含了此内置方法(来自文档):

ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs
'''The formfield_for_foreignkey method on a ModelAdmin allows you to 
   override the default formfield for a foreign keys field. For example, 
   to return a subset of objects for this foreign key field based on the
   user:'''

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

一种更精细的方法(例如,创建用户可以访问的前端管理界面)是将ModelAdmin子类化,然后更改下面的方法。最终结果是一个用户界面,该界面仅向他们显示与他们相关的内容,同时允许您(超级用户)查看所有内容。

我已经重写了四种方法,前两种使用户无法删除任何东西,并且它还从管理站点中删除了删除按钮。

第三个替代会过滤包含引用的所有查询(在示例中为“用户”或“豪猪”(仅作为说明))。

最后一个替代项过滤模型中的任何外键字段,以过滤与基本查询集相同的可用选项。

这样,您可以呈现一个易于管理的前端管理站点,该站点允许用户与自己的对象混淆,而您不必记住键入我们上面提到的特定ModelAdmin过滤器。

class FrontEndAdmin(models.ModelAdmin):
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        super(FrontEndAdmin, self).__init__(model, admin_site)

删除“删除”按钮:

    def get_actions(self, request):
        actions = super(FrontEndAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

禁止删除权限

    def has_delete_permission(self, request, obj=None):
        return False

筛选可以在管理站点上查看的对象:

    def get_queryset(self, request):
        if request.user.is_superuser:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()
            return qs

        else:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()

            if hasattr(self.model, user’):
                return qs.filter(user=request.user)
            if hasattr(self.model, porcupine’):
                return qs.filter(porcupine=request.user.porcupine)
            else:
                return qs

过滤管理站点上所有外键字段的选择:

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.employee.is_superuser:
            return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

        else:
            if hasattr(db_field.rel.to, 'user'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
            if hasattr(db_field.rel.to, 'porcupine'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
            return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)

This is simple, and works with Django 1.4:

class ClientAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ClientAdminForm, self).__init__(*args, **kwargs)
        # access object through self.instance...
        self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)

class ClientAdmin(admin.ModelAdmin):
    form = ClientAdminForm
    ....

You don’t need to specify this in a form class, but can do it directly in the ModelAdmin, as Django already includes this built-in method on the ModelAdmin (from the docs):

ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)¶
'''The formfield_for_foreignkey method on a ModelAdmin allows you to 
   override the default formfield for a foreign keys field. For example, 
   to return a subset of objects for this foreign key field based on the
   user:'''

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

An even niftier way to do this (for example in creating a front-end admin interface that users can access) is to subclass the ModelAdmin and then alter the methods below. The net result is a user interface that ONLY shows them content that is related to them, while allowing you (a super-user) to see everything.

I’ve overridden four methods, the first two make it impossible for a user to delete anything, and it also removes the delete buttons from the admin site.

The third override filters any query that contains a reference to (in the example ‘user’ or ‘porcupine’ (just as an illustration).

The last override filters any foreignkey field in the model to filter the choices available the same as the basic queryset.

In this way, you can present an easy to manage front-facing admin site that allows users to mess with their own objects, and you don’t have to remember to type in the specific ModelAdmin filters we talked about above.

class FrontEndAdmin(models.ModelAdmin):
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        super(FrontEndAdmin, self).__init__(model, admin_site)

remove ‘delete’ buttons:

    def get_actions(self, request):
        actions = super(FrontEndAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

prevents delete permission

    def has_delete_permission(self, request, obj=None):
        return False

filters objects that can be viewed on the admin site:

    def get_queryset(self, request):
        if request.user.is_superuser:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()
            return qs

        else:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()

            if hasattr(self.model, ‘user’):
                return qs.filter(user=request.user)
            if hasattr(self.model, ‘porcupine’):
                return qs.filter(porcupine=request.user.porcupine)
            else:
                return qs

filters choices for all foreignkey fields on the admin site:

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.employee.is_superuser:
            return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

        else:
            if hasattr(db_field.rel.to, 'user'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
            if hasattr(db_field.rel.to, 'porcupine'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
            return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)

回答 3

为此,可以使用通用视图,例如CreateView …

class AddPhotoToProject(CreateView):
    """
    a view where a user can associate a photo with a project
    """
    model = Connection
    form_class = CreateConnectionForm


    def get_context_data(self, **kwargs):
        context = super(AddPhotoToProject, self).get_context_data(**kwargs)
        context['photo'] = self.kwargs['pk']
        context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)
        return context
    def form_valid(self, form):
        pobj = Photo.objects.get(pk=self.kwargs['pk'])
        obj = form.save(commit=False)
        obj.photo = pobj
        obj.save()

        return_json = {'success': True}

        if self.request.is_ajax():

            final_response = json.dumps(return_json)
            return HttpResponse(final_response)

        else:

            messages.success(self.request, 'photo was added to project!')
            return HttpResponseRedirect(reverse('MyPhotos'))

最重要的部分

    context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)

在这里阅读我的文章

To do this with a generic view, like CreateView…

class AddPhotoToProject(CreateView):
    """
    a view where a user can associate a photo with a project
    """
    model = Connection
    form_class = CreateConnectionForm


    def get_context_data(self, **kwargs):
        context = super(AddPhotoToProject, self).get_context_data(**kwargs)
        context['photo'] = self.kwargs['pk']
        context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)
        return context
    def form_valid(self, form):
        pobj = Photo.objects.get(pk=self.kwargs['pk'])
        obj = form.save(commit=False)
        obj.photo = pobj
        obj.save()

        return_json = {'success': True}

        if self.request.is_ajax():

            final_response = json.dumps(return_json)
            return HttpResponse(final_response)

        else:

            messages.success(self.request, 'photo was added to project!')
            return HttpResponseRedirect(reverse('MyPhotos'))

the most important part of that…

    context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)

, read my post here


回答 4

如果尚未创建表单,但想更改查询集,则可以执行以下操作:

formmodel.base_fields['myfield'].queryset = MyModel.objects.filter(...)

当您使用通用视图时,这非常有用!

If you haven’t created the form and want to change the queryset you can do:

formmodel.base_fields['myfield'].queryset = MyModel.objects.filter(...)

This is pretty useful when you are using generic views!


回答 5

因此,我确实试图理解这一点,但是Django似乎并没有使它变得非常简单。我不是那么傻,但是我看不到任何(某种程度上)简单的解决方案。

我发现必须重写Admin视图来处理这类事情通常很丑陋,而且我发现的每个示例都从未完全适用于Admin视图。

这是我制作的模型的常见情况,令我感到震惊的是,没有明显的解决方案…

我有这些课:

# models.py
class Company(models.Model):
    # ...
class Contract(models.Model):
    company = models.ForeignKey(Company)
    locations = models.ManyToManyField('Location')
class Location(models.Model):
    company = models.ForeignKey(Company)

在为公司设置管理员时,这会产生问题,因为它同时具有合同和位置的内联,并且根据您当前正在编辑的公司未正确过滤合同的“位置”的m2m选项。

简而言之,我需要一些管理选项来执行以下操作:

# admin.py
class LocationInline(admin.TabularInline):
    model = Location
class ContractInline(admin.TabularInline):
    model = Contract
class CompanyAdmin(admin.ModelAdmin):
    inlines = (ContractInline, LocationInline)
    inline_filter = dict(Location__company='self')

最终,我不会在乎过滤过程是放在基本的CompanyAdmin上还是放在ContractInline上。(将其放在内联中更有意义,但是很难将基本合同称为“自身”。)

有没有人知道像这个急需的捷径一样简单的东西?早在我让PHP管理员进行此类工作时,这被视为基本功能!实际上,它始终是自动的,如果您真的不想要它,则必须将其禁用!

So, I’ve really tried to understand this, but it seems that Django still doesn’t make this very straightforward. I’m not all that dumb, but I just can’t see any (somewhat) simple solution.

I find it generally pretty ugly to have to override the Admin views for this sort of thing, and every example I find never fully applies to the Admin views.

This is such a common circumstance with the models I make that I find it appalling that there’s no obvious solution to this…

I’ve got these classes:

# models.py
class Company(models.Model):
    # ...
class Contract(models.Model):
    company = models.ForeignKey(Company)
    locations = models.ManyToManyField('Location')
class Location(models.Model):
    company = models.ForeignKey(Company)

This creates a problem when setting up the Admin for Company, because it has inlines for both Contract and Location, and Contract’s m2m options for Location are not properly filtered according to the Company that you’re currently editing.

In short, I would need some admin option to do something like this:

# admin.py
class LocationInline(admin.TabularInline):
    model = Location
class ContractInline(admin.TabularInline):
    model = Contract
class CompanyAdmin(admin.ModelAdmin):
    inlines = (ContractInline, LocationInline)
    inline_filter = dict(Location__company='self')

Ultimately I wouldn’t care if the filtering process was placed on the base CompanyAdmin, or if it was placed on the ContractInline. (Placing it on the inline makes more sense, but it makes it hard to reference the base Contract as ‘self’.)

Is there anyone out there who knows of something as straightforward as this badly needed shortcut? Back when I made PHP admins for this sort of thing, this was considered basic functionality! In fact, it was always automatic, and had to be disabled if you really didn’t want it!


回答 6

一种更公共的方法是在Admin类中调用get_form。它也适用于非数据库字段。例如,这里我在表单上有一个名为“ _terminal_list”的字段,在特殊情况下可用于从get_list(request)中选择多个终端项,然后根据request.user进行过滤:

class ChangeKeyValueForm(forms.ModelForm):  
    _terminal_list = forms.ModelMultipleChoiceField( 
queryset=Terminal.objects.all() )

    class Meta:
        model = ChangeKeyValue
        fields = ['_terminal_list', 'param_path', 'param_value', 'scheduled_time',  ] 

class ChangeKeyValueAdmin(admin.ModelAdmin):
    form = ChangeKeyValueForm
    list_display = ('terminal','task_list', 'plugin','last_update_time')
    list_per_page =16

    def get_form(self, request, obj = None, **kwargs):
        form = super(ChangeKeyValueAdmin, self).get_form(request, **kwargs)
        qs, filterargs = Terminal.get_list(request)
        form.base_fields['_terminal_list'].queryset = qs
        return form

A more public way is by calling get_form in Admin classes. It also works for non-database fields too. For example here i have a field called ‘_terminal_list’ on the form that can be used in special cases for choosing several terminal items from get_list(request), then filtering based on request.user:

class ChangeKeyValueForm(forms.ModelForm):  
    _terminal_list = forms.ModelMultipleChoiceField( 
queryset=Terminal.objects.all() )

    class Meta:
        model = ChangeKeyValue
        fields = ['_terminal_list', 'param_path', 'param_value', 'scheduled_time',  ] 

class ChangeKeyValueAdmin(admin.ModelAdmin):
    form = ChangeKeyValueForm
    list_display = ('terminal','task_list', 'plugin','last_update_time')
    list_per_page =16

    def get_form(self, request, obj = None, **kwargs):
        form = super(ChangeKeyValueAdmin, self).get_form(request, **kwargs)
        qs, filterargs = Terminal.get_list(request)
        form.base_fields['_terminal_list'].queryset = qs
        return form

Django order_by查询集,升序和降序

问题:Django order_by查询集,升序和降序

如何按日期降序对django中的查询集进行排序?

Reserved.objects.all().filter(client=client_id).order_by('check_in')

我只想从所有的check_in日期按降序过滤。

How can I order by descending my query set in django by date?

Reserved.objects.all().filter(client=client_id).order_by('check_in')

I just want to filter from descending all the Reserved by check_in date.


回答 0

Reserved.objects.filter(client=client_id).order_by('-check_in')

注意-之前check_in

Django文档

Reserved.objects.filter(client=client_id).order_by('-check_in')

Notice the - before check_in.

Django Documentation


回答 1

Reserved.objects.filter(client=client_id).order_by('-check_in')

“ check_in”前面的连字符“-”表示降序。隐含升序。

我们不必在filter()之前添加all()。那仍然可以工作,但是只需要从根QuerySet中获取所有对象时就需要添加all()。

此处的更多信息:https : //docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters

Reserved.objects.filter(client=client_id).order_by('-check_in')

A hyphen “-” in front of “check_in” indicates descending order. Ascending order is implied.

We don’t have to add an all() before filter(). That would still work, but you only need to add all() when you want all objects from the root QuerySet.

More on this here: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters


回答 2

您还可以使用以下说明:

Reserved.objects.filter(client=client_id).order_by('check_in').reverse()

You can also use the following instruction:

Reserved.objects.filter(client=client_id).order_by('check_in').reverse()

回答 3

对于升序:

Reserved.objects.filter(client=client_id).order_by('check_in')

对于降序:

1.  Reserved.objects.filter(client=client_id).order_by('-check_in')

要么

2.  Reserved.objects.filter(client=client_id).order_by('check_in')[::-1]

for ascending order:

Reserved.objects.filter(client=client_id).order_by('check_in')

for descending order:

1.  Reserved.objects.filter(client=client_id).order_by('-check_in')

or

2.  Reserved.objects.filter(client=client_id).order_by('check_in')[::-1]

回答 4

它可以删除.all()

Reserved.objects.filter(client=client_id).order_by('-check_in')

It works removing .all():

Reserved.objects.filter(client=client_id).order_by('-check_in')

回答 5

添加-将以降序排列。您还可以通过向模型的元数据添加默认顺序来进行设置。这意味着当您执行查询时,只需执行MyModel.objects.all(),它将以正确的顺序显示。

class MyModel(models.Model):

    check_in = models.DateField()

    class Meta:
        ordering = ('-check_in',)

Adding the – will order it in descending order. You can also set this by adding a default ordering to the meta of your model. This will mean that when you do a query you just do MyModel.objects.all() and it will come out in the correct order.

class MyModel(models.Model):

    check_in = models.DateField()

    class Meta:
        ordering = ('-check_in',)

回答 6

  1. 升序

    Reserved.objects.all().filter(client=client_id).order_by('check_in')
  2. 降序

    Reserved.objects.all().filter(client=client_id).order_by('-check_in')

- (连字符)用于指示降序。

  1. Ascending order

    Reserved.objects.all().filter(client=client_id).order_by('check_in')
    
  2. Descending order

    Reserved.objects.all().filter(client=client_id).order_by('-check_in')
    

- (hyphen) is used to indicate descending order here.


回答 7

这对我有用。

latestsetuplist = SetupTemplate.objects.order_by('-creationTime')[:10][::1]

This is working for me.

latestsetuplist = SetupTemplate.objects.order_by('-creationTime')[:10][::1]

回答 8

67

Reserved.objects.filter(client = client_id).order_by(’-check_in’)

“-”表示降序,对于升序,仅提供类属性

67

Reserved.objects.filter(client=client_id).order_by(‘-check_in’)

‘-‘ is indicates Descending order and for Ascending order just give class attribute


Django datetime问题(default = datetime.now())

问题:Django datetime问题(default = datetime.now())

我有下面的数据库模型:

from datetime import datetime    

class TermPayment(models.Model):
    # I have excluded fields that are irrelevant to the question
    date = models.DateTimeField(default=datetime.now(), blank=True)

我使用以下方法添加新实例:

tp = TermPayment.objects.create(**kwargs)

我的问题:数据库中的所有记录在date字段中都具有相同的值,这是第一次付款的日期。服务器重新启动后,一个记录具有新的日期,其他记录与第一个相同。看起来好像有些数据被缓存了,但是我找不到位置。

数据库:mysql 5.1.25

Django v1.1.1

I have the below db model:

from datetime import datetime    

class TermPayment(models.Model):
    # I have excluded fields that are irrelevant to the question
    date = models.DateTimeField(default=datetime.now(), blank=True)

I add a new instance by using the below:

tp = TermPayment.objects.create(**kwargs)

My issue: all records in database have the same value in date field, which is the date of the first payment. After the server restarts, one record has the new date and the other records have the same as the first. It looks as if some data is cached, but I can’t find where.

database: mysql 5.1.25

django v1.1.1


回答 0

似乎datetime.now()是在定义模型时(而不是每次添加记录时)正在评估。

Django具有一项功能,可以完成您正在尝试做的事情:

date = models.DateTimeField(auto_now_add=True, blank=True)

要么

date = models.DateTimeField(default=datetime.now, blank=True)

第二个示例与当前示例之间的区别在于缺少括号。通过datetime.now不带括号的传递,就传递了实际的函数,每次添加记录时都会调用该函数。如果传递它datetime.now(),那么您只是在评估函数并将其返回值传递给它。

Django的模型字段参考中提供了更多信息。

it looks like datetime.now() is being evaluated when the model is defined, and not each time you add a record.

Django has a feature to accomplish what you are trying to do already:

date = models.DateTimeField(auto_now_add=True, blank=True)

or

date = models.DateTimeField(default=datetime.now, blank=True)

The difference between the second example and what you currently have is the lack of parentheses. By passing datetime.now without the parentheses, you are passing the actual function, which will be called each time a record is added. If you pass it datetime.now(), then you are just evaluating the function and passing it the return value.

More information is available at Django’s model field reference


回答 1

而不是使用datetime.now您应该真正使用from django.utils.timezone import now

参考:

所以去这样的事情:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)

Instead of using datetime.now you should be really using from django.utils.timezone import now

Reference:

so go for something like this:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)

回答 2

从django模型默认字段的文档中

字段的默认值。这可以是值或可调用对象。如果可以调用,则每次创建新对象时都会调用它。

因此,以下应该起作用:

date = models.DateTimeField(default=datetime.now,blank=True)

From the documentation on the django model default field:

The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.

Therefore following should work:

date = models.DateTimeField(default=datetime.now,blank=True)

回答 3

大卫的回答正确。括号()使得每次评估模型时都调用可调用的 timezone.now()。如果从timezone.now()(如果使用朴素的datetime对象,则从datezone.now()或datetime.now())中删除()来做到这一点:

default=timezone.now

然后它将按您期望的那样工作:
新对象在创建时会收到当前日期,但是每次您进行manage.py makemigrations / migrate时都不会覆盖该日期。

我刚遇到这个。非常感谢David。

David had the right answer. The parenthesis () makes it so that the callable timezone.now() is called every time the model is evaluated. If you remove the () from timezone.now() (or datetime.now(), if using the naive datetime object) to make it just this:

default=timezone.now

Then it will work as you expect:
New objects will receive the current date when they are created, but the date won’t be overridden every time you do manage.py makemigrations/migrate.

I just encountered this. Much thanks to David.


回答 4

datetime.now()创建类时评估,而不是在将新记录添加到数据库时评估。

要实现您想要的目标,请将该字段定义为:

date = models.DateTimeField(auto_now_add=True)

这样,该date字段将被设置为每个新记录的当前日期。

The datetime.now() is evaluated when the class is created, not when new record is being added to the database.

To achieve what you want define this field as:

date = models.DateTimeField(auto_now_add=True)

This way the date field will be set to current date for each new record.


回答 5

这个问题的答案实际上是错误的。

自动填写值(auto_now / auto_now_add与默认值不同)。默认值实际上是用户看到的全新对象。我通常要做的是:

date = models.DateTimeField(default=datetime.now, editable=False,)

确保,如果您试图在“管理”页面中表示此信息,请确保将其列为“ read_only”并引用字段名称

read_only = 'date'

同样,我这样做是因为我的默认值通常是不可编辑的,除非另有说明,否则管理页面会忽略不可编辑的内容。但是,设置默认值和实现关键的auto_add之间肯定有区别。测试一下!

The answer to this one is actually wrong.

Auto filling in the value (auto_now/auto_now_add isn’t the same as default). The default value will actually be what the user sees if its a brand new object. What I typically do is:

date = models.DateTimeField(default=datetime.now, editable=False,)

Make sure, if your trying to represent this in an Admin page, that you list it as ‘read_only’ and reference the field name

read_only = 'date'

Again, I do this since my default value isn’t typically editable, and Admin pages ignore non-editables unless specified otherwise. There is certainly a difference however between setting a default value and implementing the auto_add which is key here. Test it out!


回答 6

datetime.now()在实例化您的类时被评估一次。尝试删除括号,以便datetime.now返回函数并评估THEN。我在为设置默认值时遇到了同样的问题,DateTimeField在此处写下了解决方案。

datetime.now() is being evaluated once, when your class is instantiated. Try removing the parenthesis so that the function datetime.now is returned and THEN evaluated. I had the same issue with setting default values for my DateTimeFields and wrote up my solution here.


回答 7

从Python语言参考中的“ 函数定义”下

执行功能定义时,将评估默认参数值。这意味着在定义函数时,表达式将被计算一次,并且每次调用都使用相同的“预先计算”值。

幸运的是,如果您将auto_now参数用作,则Django可以执行您想要的操作DateTimeField

date = models.DateTimeField(auto_now=True)

请参阅Django文档中的DateTimeField

From the Python language reference, under Function definitions:

Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that that same “pre-computed” value is used for each call.

Fortunately, Django has a way to do what you want, if you use the auto_now argument for the DateTimeField:

date = models.DateTimeField(auto_now=True)

See the Django docs for DateTimeField.


回答 8

在Django 3.0中 auto_now_add似乎可以使用auto_now

reg_date=models.DateField(auto_now=True,blank=True)

In Django 3.0 auto_now_add seems to work with auto_now

reg_date=models.DateField(auto_now=True,blank=True)


将我的virtualenv目录放在git存储库中是否不好?

问题:将我的virtualenv目录放在git存储库中是否不好?

我正在考虑将virtualenv用于我在git存储库中创建的Django Web应用程序中。这似乎是使部署变得如此简单的一种简单方法。我为什么不应该这样做?

I’m thinking about putting the virtualenv for a Django web app I am making inside my git repository for the app. It seems like an easy way to keep deploy’s simple and easy. Is there any reason why I shouldn’t do this?


回答 0

我通常pip freeze将所需的软件包放入requirements.txt文件中,然后将其添加到存储库中。我试图思考为什么您要存储整个virtualenv的方法,但是我不能。

I use pip freeze to get the packages I need into a requirements.txt file and add that to my repository. I tried to think of a way of why you would want to store the entire virtualenv, but I could not.


回答 1

正如您指出的那样,将virtualenv目录存储在git中将允许您仅通过执行git clone(加上安装和配置Apache / mod_wsgi)来部署整个应用程序。这种方法的一个潜在的重大问题是,在Linux上,完整路径在venv的activate,django-admin.py,easy_install和pip脚本中进行了硬编码。这意味着,如果您想使用不同的路径,也许要在同一台服务器上运行多个虚拟主机,那么您的virtualenv将无法完全正常工作。我认为该网站实际上可能在这些文件中使用了错误的路径,但下一次尝试运行pip时会遇到问题。

已经给出的解决方案是在git中存储足够的信息,以便在部署过程中可以创建virtualenv并进行必要的pip安装。人们通常会跑步pip freeze以获取列表,然后将其存储在名为requirements.txt的文件中。可以加载pip install -r requirements.txt。RyanBrady已经展示了如何在一行中将deploy语句字符串化:

# before 15.1.0
virtualenv --no-site-packages --distribute .env &&\
    source .env/bin/activate &&\
    pip install -r requirements.txt

# after deprecation of some arguments in 15.1.0
virtualenv .env && source .env/bin/activate && pip install -r requirements.txt

就个人而言,我只是将它们放在执行git clone或git pull之后运行的shell脚本中。

存储virtualenv目录还使处理pip升级变得有些棘手,因为您必须手动添加/删除并提交升级后的文件。使用requirements.txt文件,您只需更改requirements.txt中的相应行并重新运行即可pip install -r requirements.txt。如前所述,这还减少了“提交垃圾邮件”。

Storing the virtualenv directory inside git will, as you noted, allow you to deploy the whole app by just doing a git clone (plus installing and configuring Apache/mod_wsgi). One potentially significant issue with this approach is that on Linux the full path gets hard-coded in the venv’s activate, django-admin.py, easy_install, and pip scripts. This means your virtualenv won’t entirely work if you want to use a different path, perhaps to run multiple virtual hosts on the same server. I think the website may actually work with the paths wrong in those files, but you would have problems the next time you tried to run pip.

The solution, already given, is to store enough information in git so that during the deploy you can create the virtualenv and do the necessary pip installs. Typically people run pip freeze to get the list then store it in a file named requirements.txt. It can be loaded with pip install -r requirements.txt. RyanBrady already showed how you can string the deploy statements in a single line:

# before 15.1.0
virtualenv --no-site-packages --distribute .env &&\
    source .env/bin/activate &&\
    pip install -r requirements.txt

# after deprecation of some arguments in 15.1.0
virtualenv .env && source .env/bin/activate && pip install -r requirements.txt

Personally, I just put these in a shell script that I run after doing the git clone or git pull.

Storing the virtualenv directory also makes it a bit trickier to handle pip upgrades, as you’ll have to manually add/remove and commit the files resulting from the upgrade. With a requirements.txt file, you just change the appropriate lines in requirements.txt and re-run pip install -r requirements.txt. As already noted, this also reduces “commit spam”.


回答 2

在开始使用根据环境(例如PyCrypto)进行不同编译的库之前,我一直这样做。我的PyCrypto mac无法在Cygwin上运行,也无法在Ubuntu上运行。

管理存储库已成为一场噩梦。

无论哪种方式,我都发现管理点子冻结和需求文件比将其全部保存在git中更容易。它也更加干净,因为随着这些库的更新,您可以避免提交数千个文件的垃圾邮件…

I used to do the same until I started using libraries that are compiled differently depending on the environment such as PyCrypto. My PyCrypto mac wouldn’t work on Cygwin wouldn’t work on Ubuntu.

It becomes an utter nightmare to manage the repository.

Either way I found it easier to manage the pip freeze & a requirements file than having it all in git. It’s cleaner too since you get to avoid the commit spam for thousands of files as those libraries get updated…


回答 3

我认为出现的主要问题之一是virtualenv可能无法被其他人使用。原因是它始终使用绝对路径。因此,例如,如果您使用virtualenv,/home/lyle/myenv/它将对使用此存储库的所有其他用户都假设相同(其绝对路径必须完全相同)。您不能假定人们使用与您相同的目录结构。

更好的做法是每个人都在建立自己的环境(无论是否带有virtualenv)并在其中安装库。这也使您的代码在不同平台(Linux / Windows / Mac)上的可用性更高,这也是因为在每个平台上都不同地安装了virtualenv。

I think one of the main problems which occur is that the virtualenv might not be usable by other people. Reason is that it always uses absolute paths. So if you virtualenv was for example in /home/lyle/myenv/ it will assume the same for all other people using this repository (it must be exactly the same absolute path). You can’t presume people using the same directory structure as you.

Better practice is that everybody is setting up their own environment (be it with or without virtualenv) and installing libraries there. That also makes you code more usable over different platforms (Linux/Windows/Mac), also because virtualenv is installed different in each of them.


回答 4

我使用的基本上是David Sickmiller的答案,并且自动化程度更高。我在项目的顶层创建一个(不可执行的)文件,其名称activate如下:

[ -n "$BASH_SOURCE" ] \
    || { echo 1>&2 "source (.) this with Bash."; exit 2; }
(
    cd "$(dirname "$BASH_SOURCE")"
    [ -d .build/virtualenv ] || {
        virtualenv .build/virtualenv
        . .build/virtualenv/bin/activate
        pip install -r requirements.txt
    }
)
. "$(dirname "$BASH_SOURCE")/.build/virtualenv/bin/activate"

(根据David的回答,这是假设您正在执行,pip freeze > requirements.txt以使您的需求列表保持最新。)

以上给出了总体思路;实际的激活脚本(文档),我通常使用是有点更复杂,提供了-q(安静)选项,使用pythonpython3不可用等。

然后,可以从任何当前工作目录中获取该资源并将其正确激活,必要时首先设置虚拟环境。我的顶级测试脚本通常包含以下几行代码,因此无需开发人员先激活即可运行它:

cd "$(dirname "$0")"
[[ $VIRTUAL_ENV = $(pwd -P) ]] || . ./activate

这里的Sourcing ./activate(而不是activate)很重要,因为后者会activate在您的路径中找到其他任何路径,然后再在当前目录中找到其他路径。

I use what is basically David Sickmiller’s answer with a little more automation. I create a (non-executable) file at the top level of my project named activate with the following contents:

[ -n "$BASH_SOURCE" ] \
    || { echo 1>&2 "source (.) this with Bash."; exit 2; }
(
    cd "$(dirname "$BASH_SOURCE")"
    [ -d .build/virtualenv ] || {
        virtualenv .build/virtualenv
        . .build/virtualenv/bin/activate
        pip install -r requirements.txt
    }
)
. "$(dirname "$BASH_SOURCE")/.build/virtualenv/bin/activate"

(As per David’s answer, this assumes you’re doing a pip freeze > requirements.txt to keep your list of requirements up to date.)

The above gives the general idea; the actual activate script (documentation) that I normally use is a bit more sophisticated, offering a -q (quiet) option, using python when python3 isn’t available, etc.

This can then be sourced from any current working directory and will properly activate, first setting up the virtual environment if necessary. My top-level test script usually has code along these lines so that it can be run without the developer having to activate first:

cd "$(dirname "$0")"
[[ $VIRTUAL_ENV = $(pwd -P) ]] || . ./activate

Sourcing ./activate, not activate, is important here because the latter will find any other activate in your path before it will find the one in the current directory.


回答 5

在回购协议中包含任何与环境相关的组件或设置作为使用回购协议的关键方面之一不是一个好主意,也许是与其他开发人员共享它。这是在Windows PC(例如Win10)上设置开发环境的方式。

  1. 打开Pycharm,然后在第一页上,选择从您的源代码管理系统中检出项目(在我的情况下,我正在使用github)

  2. 在Pycharm中,导航至设置,然后选择“项目解释器”,然后选择添加新虚拟环境的选项,您可以将其称为“ venv”。

  3. 选择位于C:\ Users {user} \ AppData \ Local \ Programs \ Python \ Python36的基本python解释器(请确保根据安装的内容选择适当的Python版本)

  4. 请注意,Pycharm将创建新的虚拟环境,并在项目文件夹内的venv文件夹下复制python二进制文件和所需的库。

  5. 让Pycharm完成其扫描,因为它需要重建/刷新项目框架

  6. 从git交互中排除venv文件夹(将venv \添加到项目文件夹中的.gitignore文件)

奖励:如果您希望人们轻松(很好,几乎很容易)安装软件所需的所有库,则可以使用

pip freeze > requirements.txt

并将说明放在git上,以便人们可以使用以下命令立即下载所有必需的库。

pip install -r requirements.txt 

It’s not a good idea to include any environment-dependent component or setting in your repos as one of the key aspects of using a repo, is perhaps, sharing it with other developers. Here is how I would setup my development environment on a Windows PC (say, Win10).

  1. Open Pycharm and on the first page, choose to check out the project from your Source Control System (in my case, I am using github)

  2. In Pycharm, navigate to settings and choose “Project Interpreter” and choose the option to add a new virtual environment , you can call it “venv”.

  3. Choose the base python interpreter which is located at C:\Users{user}\AppData\Local\Programs\Python\Python36 (make sure you choose the appropriate version of Python based on what you have installed)

  4. Note that Pycharm will create the new virtual environment and copy python binaries and required libraries under your venv folder inside your project folder.

  5. Let Pycharm complete its scanning as it needs to rebuild/refresh your project skeleton

  6. exclude venv folder from your git interactions (add venv\ to .gitignore file in your project folder)

Bonus: If you want people to easily (well, almost easily) install all the libraries your software needs, you can use

pip freeze > requirements.txt

and put the instruction on your git so people can use the following command to download all required libraries at once.

pip install -r requirements.txt 

回答 6

如果您知道您的应用程序将在哪个操作系统上运行,我将为每个系统创建一个virtualenv并将其包含在我的存储库中。然后,我将让我的应用程序检测它在哪个系统上运行,并使用相应的virtualenv。

该系统可以例如使用平台模块来识别。

实际上,这就是我对自己编写的内部应用程序所做的工作,可以在需要时快速添加新系统的virtualenv。这样,我不必依靠那个点就可以成功下载我的应用程序所需的软件。我也不必担心例如psycopg2的编译我使用。

如果您不知道您的应用程序可以在哪个操作系统上运行,那么最好pip freeze按照此处其他答案中的建议使用。

If you know which operating systems your application will be running on, I would create one virtualenv for each system and include it in my repository. Then I would make my application detect which system it is running on and use the corresponding virtualenv.

The system could e.g. be identified using the platform module.

In fact, this is what I do with an in-house application I have written, and to which I can quickly add a new system’s virtualenv in case it is needed. This way, I do not have to rely on that pip will be able to successfully download the software my application requires. I will also not have to worry about compilation of e.g. psycopg2 which I use.

If you do not know which operating system your application may run on, you are probably better off using pip freeze as suggested in other answers here.


回答 7

我认为最好的办法是在存储库文件夹内的路径中安装虚拟环境,最好使用专用于该环境的子目录(当我在存储库根目录中强制安装虚拟环境时,我意外删除了我的整个项目)文件夹,好是我已将项目保存在最新版本的Github中)。

自动安装程序或文档都应将virtualenv路径指示为相对路径,这样,与他人共享项目时,您就不会遇到问题。关于软件包,使用的软件包应通过保存pip freeze -r requirements.txt

I think is that the best is to install the virtual environment in a path inside the repository folder, maybe is better inclusive to use a subdirectory dedicated to the environment (I have deleted accidentally my entire project when force installing a virtual environment in the repository root folder, good that I had the project saved in its latest version in Github).

Either the automated installer, or the documentation should indicate the virtualenv path as a relative path, this way you won’t run into problems when sharing the project with other people. About the packages, the packages used should be saved by pip freeze -r requirements.txt.


回答 8

如果您只是设置开发环境,请使用pip冻结文件,因为caz可以使git repo变得干净。

然后,如果要进行生产部署,则签入整个venv文件夹。这将使您的部署更具可重复性,不需要那些libxxx-dev软件包,并避免了Internet问题。

因此,有两个存储库。一个用于您的主要源代码,其中包括requirements.txt。还有一个env存储库,其中包含整个venv文件夹。

If you just setting up development env, then use pip freeze file, caz that makes the git repo clean.

Then if doing production deployment, then checkin the whole venv folder. That will make your deployment more reproducible, not need those libxxx-dev packages, and avoid the internet issues.

So there are two repos. One for your main source code, which includes a requirements.txt. And a env repo, which contains the whole venv folder.


如何将JSON数据转换为Python对象

问题:如何将JSON数据转换为Python对象

我想使用Python将JSON数据转换成Python对象。

我从Facebook API接收了JSON数据对象,我想将其存储在数据库中。

我当前在Django(Python)中的视图(request.POST包含JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • 这可以正常工作,但是如何处理复杂的JSON数据对象?

  • 如果我能以某种方式将这个JSON对象转换为Python对象以便于使用,会不会更好呢?

I want to use Python to convert JSON data into a Python object.

I receive JSON data objects from the Facebook API, which I want to store in my database.

My current View in Django (Python) (request.POST contains the JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • This works fine, but how do I handle complex JSON data objects?

  • Wouldn’t it be much better if I could somehow convert this JSON object into a Python object for easy use?


回答 0

您可以使用namedtuple和在一行中完成操作object_hook

import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

或者,轻松重用此方法:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

如果您希望它处理不是好的属性名称的键,请查看namedtuplerenameparameter

UPDATE

With Python3, you can do it in one line, using SimpleNamespace and object_hook:

import json
from types import SimpleNamespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
print(x.name, x.hometown.name, x.hometown.id)

OLD ANSWER (Python2)

In Python2, you can do it in one line, using namedtuple and object_hook (but it’s very slow with many nested objects):

import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

or, to reuse this easily:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

If you want it to handle keys that aren’t good attribute names, check out namedtuple‘s rename parameter.


回答 1

检查出标题为专业JSON对象解码json 模块文档。您可以使用它来将JSON对象解码为特定的Python类型。

这是一个例子:

class User(object):
    def __init__(self, name, username):
        self.name = name
        self.username = username

import json
def object_decoder(obj):
    if '__type__' in obj and obj['__type__'] == 'User':
        return User(obj['name'], obj['username'])
    return obj

json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}',
           object_hook=object_decoder)

print type(User)  # -> <type 'type'>

更新资料

如果要通过json模块访问字典中的数据,请执行以下操作:

user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']

就像普通字典一样。

Check out the section titled Specializing JSON object decoding in the json module documentation. You can use that to decode a JSON object into a specific Python type.

Here’s an example:

class User(object):
    def __init__(self, name, username):
        self.name = name
        self.username = username

import json
def object_decoder(obj):
    if '__type__' in obj and obj['__type__'] == 'User':
        return User(obj['name'], obj['username'])
    return obj

json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}',
           object_hook=object_decoder)

print type(User)  # -> <type 'type'>

Update

If you want to access data in a dictionary via the json module do this:

user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']

Just like a regular dictionary.


回答 2

这不是代码编程,但这是我最短的技巧,types.SimpleNamespace用作JSON对象的容器。

与领先的namedtuple解决方案相比,它是:

  • 可能更快/更小,因为它不会为每个对象创建一个类
  • 短一点
  • 没有rename选项,并且对于无效标识符的密钥可能有相同的限制(在幕后使用setattr

例:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)

This is not code golf, but here is my shortest trick, using types.SimpleNamespace as the container for JSON objects.

Compared to the leading namedtuple solution, it is:

  • probably faster/smaller as it does not create a class for each object
  • shorter
  • no rename option, and probably the same limitation on keys that are not valid identifiers (uses setattr under the covers)

Example:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)

回答 3

您可以尝试以下方法:

class User(object):
    def __init__(self, name, username, *args, **kwargs):
        self.name = name
        self.username = username

import json
j = json.loads(your_json)
u = User(**j)

只需创建一个新的对象,然后将参数作为映射传递即可。

You could try this:

class User(object):
    def __init__(self, name, username, *args, **kwargs):
        self.name = name
        self.username = username

import json
j = json.loads(your_json)
u = User(**j)

Just create a new Object, and pass the parameters as a map.


回答 4

这是一种快速而肮脏的JSON泡菜替代品

import json

class User:
    def __init__(self, name, username):
        self.name = name
        self.username = username

    def to_json(self):
        return json.dumps(self.__dict__)

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

# example usage
User("tbrown", "Tom Brown").to_json()
User.from_json(User("tbrown", "Tom Brown").to_json()).to_json()

Here’s a quick and dirty json pickle alternative

import json

class User:
    def __init__(self, name, username):
        self.name = name
        self.username = username

    def to_json(self):
        return json.dumps(self.__dict__)

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

# example usage
User("tbrown", "Tom Brown").to_json()
User.from_json(User("tbrown", "Tom Brown").to_json()).to_json()

回答 5

对于复杂的对象,可以使用JSON Pickle

用于将任意对象图序列化为JSON的Python库。它几乎可以使用任何Python对象并将该对象转换为JSON。此外,它可以将对象重新构造回Python。

For complex objects, you can use JSON Pickle

Python library for serializing any arbitrary object graph into JSON. It can take almost any Python object and turn the object into JSON. Additionally, it can reconstitute the object back into Python.


回答 6

如果您使用的是Python 3.5+,则可以用于jsons序列化和反序列化为普通的旧Python对象:

import jsons

response = request.POST

# You'll need your class attributes to match your dict keys, so in your case do:
response['id'] = response.pop('user_id')

# Then you can load that dict into your class:
user = jsons.load(response, FbApiUser)

user.save()

您也可以FbApiUser从继承继承,jsons.JsonSerializable以获得更多的优雅:

user = FbApiUser.from_json(response)

如果您的类由Python默认类型(例如字符串,整数,列表,日期时间等)组成,则这些示例将起作用。但是jsonslib将需要自定义类型的类型提示。

If you’re using Python 3.5+, you can use jsons to serialize and deserialize to plain old Python objects:

import jsons

response = request.POST

# You'll need your class attributes to match your dict keys, so in your case do:
response['id'] = response.pop('user_id')

# Then you can load that dict into your class:
user = jsons.load(response, FbApiUser)

user.save()

You could also make FbApiUser inherit from jsons.JsonSerializable for more elegance:

user = FbApiUser.from_json(response)

These examples will work if your class consists of Python default types, like strings, integers, lists, datetimes, etc. The jsons lib will require type hints for custom types though.


回答 7

如果您使用的是Python 3.6+,则可以使用marshmallow-dataclass。与上面列出的所有解决方案相反,它既简单又可安全输入:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})

If you are using python 3.6+, you can use marshmallow-dataclass. Contrarily to all the solutions listed above, it is both simple, and type safe:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user = User.Schema().load({"name": "Ramirez"})

回答 8

我编写了一个名为any2any的小型(反序列化)框架,该有助于在两种Python类型之间进行复杂的转换。

在您的情况下,我想您想将字典(由获取json.loads)转换为response.education ; response.name具有嵌套结构response.education.id等的复杂对象……所以这正是该框架的目的。该文档尚不完善,但是通过使用any2any.simple.MappingToObject,您应该可以很轻松地做到这一点。请询问是否需要帮助。

I have written a small (de)serialization framework called any2any that helps doing complex transformations between two Python types.

In your case, I guess you want to transform from a dictionary (obtained with json.loads) to an complex object response.education ; response.name, with a nested structure response.education.id, etc … So that’s exactly what this framework is made for. The documentation is not great yet, but by using any2any.simple.MappingToObject, you should be able to do that very easily. Please ask if you need help.


回答 9

改善lovasoa的很好答案。

如果您使用的是python 3.6+,则可以使用:
pip install marshmallow-enum
pip install marshmallow-dataclass

它简单且类型安全。

您可以使用string-json转换类,反之亦然:

从对象到字符串Json:

    from marshmallow_dataclass import dataclass
    user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
    user_json = User.Schema().dumps(user)
    user_json_str = user_json.data

从String Json到Object:

    json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
    user, err = User.Schema().loads(json_str)
    print(user,flush=True)

类定义:

class OrderStatus(Enum):
    CREATED = 'Created'
    PENDING = 'Pending'
    CONFIRMED = 'Confirmed'
    FAILED = 'Failed'

@dataclass
class User:
    def __init__(self, name, orderId, productName, quantity, status):
        self.name = name
        self.orderId = orderId
        self.productName = productName
        self.quantity = quantity
        self.status = status

    name: str
    orderId: str
    productName: str
    quantity: int
    status: OrderStatus

Improving the lovasoa’s very good answer.

If you are using python 3.6+, you can use:
pip install marshmallow-enum and
pip install marshmallow-dataclass

Its simple and type safe.

You can transform your class in a string-json and vice-versa:

From Object to String Json:

    from marshmallow_dataclass import dataclass
    user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
    user_json = User.Schema().dumps(user)
    user_json_str = user_json.data

From String Json to Object:

    json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
    user, err = User.Schema().loads(json_str)
    print(user,flush=True)

Class definitions:

class OrderStatus(Enum):
    CREATED = 'Created'
    PENDING = 'Pending'
    CONFIRMED = 'Confirmed'
    FAILED = 'Failed'

@dataclass
class User:
    def __init__(self, name, orderId, productName, quantity, status):
        self.name = name
        self.orderId = orderId
        self.productName = productName
        self.quantity = quantity
        self.status = status

    name: str
    orderId: str
    productName: str
    quantity: int
    status: OrderStatus

回答 10

由于没有人像我一样提供答案,因此我将在此处发布。

这是一个强大的类,它可以很容易地来回转换JSON之间strdict我从复制我的回答另一个问题

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)

        self.from_dict(d)

    def from_dict(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def to_dict(self):
        d = {}
        for key, value in self.__dict__.items():
            if type(value) is PyJSON:
                value = value.to_dict()
            d[key] = value
        return d

    def __repr__(self):
        return str(self.to_dict())

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

json_str = """... json string ..."""

py_json = PyJSON(json_str)

Since noone provided an answer quite like mine, I am going to post it here.

It is a robust class that can easily convert back and forth between json str and dict that I have copied from my answer to another question:

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)

        self.from_dict(d)

    def from_dict(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def to_dict(self):
        d = {}
        for key, value in self.__dict__.items():
            if type(value) is PyJSON:
                value = value.to_dict()
            d[key] = value
        return d

    def __repr__(self):
        return str(self.to_dict())

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

json_str = """... json string ..."""

py_json = PyJSON(json_str)

回答 11

稍微修改@DS响应以从文件加载:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def load_data(file_name):
  with open(file_name, 'r') as file_data:
    return file_data.read().replace('\n', '')
def json2obj(file_name): return json.loads(load_data(file_name), object_hook=_json_object_hook)

一件事:这无法加载前面带有数字的项目。像这样:

{
  "1_first_item": {
    "A": "1",
    "B": "2"
  }
}

因为“ 1_first_item”不是有效的python字段名称。

Modifying @DS response a bit, to load from a file:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def load_data(file_name):
  with open(file_name, 'r') as file_data:
    return file_data.read().replace('\n', '')
def json2obj(file_name): return json.loads(load_data(file_name), object_hook=_json_object_hook)

One thing: this cannot load items with numbers ahead. Like this:

{
  "1_first_item": {
    "A": "1",
    "B": "2"
  }
}

Because “1_first_item” is not a valid python field name.


回答 12

在寻找解决方案时,我偶然发现了此博客文章:https : //blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

它使用与先前答案相同的技术,但使用了装饰器。我发现有用的另一件事是事实,它在反序列化结束时返回一个类型化的对象

class JsonConvert(object):
    class_mappings = {}

    @classmethod
    def class_mapper(cls, d):
        for keys, cls in clsself.mappings.items():
            if keys.issuperset(d.keys()):   # are all required arguments present?
                return cls(**d)
        else:
            # Raise exception instead of silently returning None
            raise ValueError('Unable to find a matching class for object: {!s}'.format(d))

    @classmethod
    def complex_handler(cls, Obj):
        if hasattr(Obj, '__dict__'):
            return Obj.__dict__
        else:
            raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)))

    @classmethod
    def register(cls, claz):
        clsself.mappings[frozenset(tuple([attr for attr,val in cls().__dict__.items()]))] = cls
        return cls

    @classmethod
    def to_json(cls, obj):
        return json.dumps(obj.__dict__, default=cls.complex_handler, indent=4)

    @classmethod
    def from_json(cls, json_str):
        return json.loads(json_str, object_hook=cls.class_mapper)

用法:

@JsonConvert.register
class Employee(object):
    def __init__(self, Name:int=None, Age:int=None):
        self.Name = Name
        self.Age = Age
        return

@JsonConvert.register
class Company(object):
    def __init__(self, Name:str="", Employees:[Employee]=None):
        self.Name = Name
        self.Employees = [] if Employees is None else Employees
        return

company = Company("Contonso")
company.Employees.append(Employee("Werner", 38))
company.Employees.append(Employee("Mary"))

as_json = JsonConvert.to_json(company)
from_json = JsonConvert.from_json(as_json)
as_json_from_json = JsonConvert.to_json(from_json)

assert(as_json_from_json == as_json)

print(as_json_from_json)

While searching for a solution, I’ve stumbled upon this blog post: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

It uses the same technique as stated in previous answers but with a usage of decorators. Another thing I found useful is the fact that it returns a typed object at the end of deserialisation

class JsonConvert(object):
    class_mappings = {}

    @classmethod
    def class_mapper(cls, d):
        for keys, cls in clsself.mappings.items():
            if keys.issuperset(d.keys()):   # are all required arguments present?
                return cls(**d)
        else:
            # Raise exception instead of silently returning None
            raise ValueError('Unable to find a matching class for object: {!s}'.format(d))

    @classmethod
    def complex_handler(cls, Obj):
        if hasattr(Obj, '__dict__'):
            return Obj.__dict__
        else:
            raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)))

    @classmethod
    def register(cls, claz):
        clsself.mappings[frozenset(tuple([attr for attr,val in cls().__dict__.items()]))] = cls
        return cls

    @classmethod
    def to_json(cls, obj):
        return json.dumps(obj.__dict__, default=cls.complex_handler, indent=4)

    @classmethod
    def from_json(cls, json_str):
        return json.loads(json_str, object_hook=cls.class_mapper)

Usage:

@JsonConvert.register
class Employee(object):
    def __init__(self, Name:int=None, Age:int=None):
        self.Name = Name
        self.Age = Age
        return

@JsonConvert.register
class Company(object):
    def __init__(self, Name:str="", Employees:[Employee]=None):
        self.Name = Name
        self.Employees = [] if Employees is None else Employees
        return

company = Company("Contonso")
company.Employees.append(Employee("Werner", 38))
company.Employees.append(Employee("Mary"))

as_json = JsonConvert.to_json(company)
from_json = JsonConvert.from_json(as_json)
as_json_from_json = JsonConvert.to_json(from_json)

assert(as_json_from_json == as_json)

print(as_json_from_json)

回答 13

稍微扩展一下DS的答案,如果您需要对象可变(不包括namedtuple),则可以使用recordclass库而不是namedtuple

import json
from recordclass import recordclass

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse into a mutable object
x = json.loads(data, object_hook=lambda d: recordclass('X', d.keys())(*d.values()))

然后可以使用simplejson轻松地将修改后的对象转换回json :

x.name = "John Doe"
new_json = simplejson.dumps(x)

Expanding on DS’s answer a bit, if you need the object to be mutable (which namedtuple is not), you can use the recordclass library instead of namedtuple:

import json
from recordclass import recordclass

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse into a mutable object
x = json.loads(data, object_hook=lambda d: recordclass('X', d.keys())(*d.values()))

The modified object can then be converted back to json very easily using simplejson:

x.name = "John Doe"
new_json = simplejson.dumps(x)

回答 14

如果您使用的是Python 3.6或更高版本,则可以看看squema-一种用于静态类型数据结构的轻量级模块。它使您的代码易于阅读,同时无需任何额外工作即可提供简单的数据验证,转换和序列化。您可以将其视为命名元组和数据类的更复杂,更自以为是的替代方案。使用方法如下:

from uuid import UUID
from squema import Squema


class FbApiUser(Squema):
    id: UUID
    age: int
    name: str

    def save(self):
        pass


user = FbApiUser(**json.loads(response))
user.save()

If you’re using Python 3.6 or newer, you could have a look at squema – a lightweight module for statically typed data structures. It makes your code easy to read while at the same time providing simple data validation, conversion and serialization without extra work. You can think of it as a more sophisticated and opinionated alternative to namedtuples and dataclasses. Here’s how you could use it:

from uuid import UUID
from squema import Squema


class FbApiUser(Squema):
    id: UUID
    age: int
    name: str

    def save(self):
        pass


user = FbApiUser(**json.loads(response))
user.save()

回答 15

我正在寻找一种可与recordclass.RecordClass,支持嵌套对象同时适用于json序列化和json反序列化的解决方案。

扩展DS的答案,并扩展BeneStr的解决方案,我想到了以下似乎可行的方法:

码:

import json
import recordclass

class NestedRec(recordclass.RecordClass):
    a : int = 0
    b : int = 0

class ExampleRec(recordclass.RecordClass):
    x : int       = None
    y : int       = None
    nested : NestedRec = NestedRec()

class JsonSerializer:
    @staticmethod
    def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
        return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)

    @staticmethod
    def loads(s, klass):
        return JsonSerializer.__dict_to_obj(klass, json.loads(s))

    @staticmethod
    def __obj_to_dict(obj):
        if hasattr(obj, "_asdict"):
            return obj._asdict()
        else:
            return json.JSONEncoder().default(obj)

    @staticmethod
    def __dict_to_obj(klass, s_dict):
        kwargs = {
            key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
                for key,cls in klass.__annotations__.items() \
                    if s_dict is not None and key in s_dict
        }
        return klass(**kwargs)

用法:

example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )

#Serialize to JSON

json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
#  "x": 10,
#  "y": 20,
#  "nested": {
#    "a": 30,
#    "b": 40
#  }
#}

# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1

json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
#  "x": 11,
#  "y": 21,
#  "nested": {
#    "a": 31,
#    "b": 41
#  }
#}

I was searching for a solution that worked with recordclass.RecordClass, supports nested objects and works for both json serialization and json deserialization.

Expanding on DS’s answer, and expanding on solution from BeneStr, I came up with the following that seems to work:

Code:

import json
import recordclass

class NestedRec(recordclass.RecordClass):
    a : int = 0
    b : int = 0

class ExampleRec(recordclass.RecordClass):
    x : int       = None
    y : int       = None
    nested : NestedRec = NestedRec()

class JsonSerializer:
    @staticmethod
    def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
        return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)

    @staticmethod
    def loads(s, klass):
        return JsonSerializer.__dict_to_obj(klass, json.loads(s))

    @staticmethod
    def __obj_to_dict(obj):
        if hasattr(obj, "_asdict"):
            return obj._asdict()
        else:
            return json.JSONEncoder().default(obj)

    @staticmethod
    def __dict_to_obj(klass, s_dict):
        kwargs = {
            key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
                for key,cls in klass.__annotations__.items() \
                    if s_dict is not None and key in s_dict
        }
        return klass(**kwargs)

Usage:

example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )

#Serialize to JSON

json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
#  "x": 10,
#  "y": 20,
#  "nested": {
#    "a": 30,
#    "b": 40
#  }
#}

# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1

json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
#  "x": 11,
#  "y": 21,
#  "nested": {
#    "a": 31,
#    "b": 41
#  }
#}

回答 16

此处给出的答案未返回正确的对象类型,因此我在下面创建了这些方法。如果您尝试将更多字段添加到给定JSON中不存在的类,它们也会失败:

def dict_to_class(class_name: Any, dictionary: dict) -> Any:
    instance = class_name()
    for key in dictionary.keys():
        setattr(instance, key, dictionary[key])
    return instance


def json_to_class(class_name: Any, json_string: str) -> Any:
    dict_object = json.loads(json_string)
    return dict_to_class(class_name, dict_object)

The answers given here does not return the correct object type, hence I created these methods below. They also fail if you try to add more fields to the class that does not exist in the given JSON:

def dict_to_class(class_name: Any, dictionary: dict) -> Any:
    instance = class_name()
    for key in dictionary.keys():
        setattr(instance, key, dictionary[key])
    return instance


def json_to_class(class_name: Any, json_string: str) -> Any:
    dict_object = json.loads(json_string)
    return dict_to_class(class_name, dict_object)

回答 17

Python3.x

我所能达到的最好的方法就是这个。
注意,此代码也对待set()。
这种方法是通用的,只需要扩展类(在第二个示例中)。
请注意,我只是对文件进行处理,但是可以根据自己的喜好修改行为。

但是,这是CoDec。

通过更多的工作,您可以用其他方式构造您的类。我假定使用默认的构造函数来实例化它,然后更新类dict。

import json
import collections


class JsonClassSerializable(json.JSONEncoder):

    REGISTERED_CLASS = {}

    def register(ctype):
        JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in self.REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = self.REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


JsonClassSerializable.register(C)


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


JsonClassSerializable.register(B)


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()

JsonClassSerializable.register(A)

A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)

编辑

通过更多的研究,我找到了一种使用元类进行泛化而无需SUPERCLASS寄存器方法调用的方法。

import json
import collections

REGISTERED_CLASS = {}

class MetaSerializable(type):

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in REGISTERED_CLASS:
            REGISTERED_CLASS[cls.__name__] = cls
        return super(MetaSerializable, cls).__call__(*args, **kwargs)


class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()


A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s

Python3.x

The best aproach I could reach with my knowledge was this.
Note that this code treat set() too.
This approach is generic just needing the extension of class (in the second example).
Note that I’m just doing it to files, but it’s easy to modify the behavior to your taste.

However this is a CoDec.

With a little more work you can construct your class in other ways. I assume a default constructor to instance it, then I update the class dict.

import json
import collections


class JsonClassSerializable(json.JSONEncoder):

    REGISTERED_CLASS = {}

    def register(ctype):
        JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in self.REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = self.REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


JsonClassSerializable.register(C)


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


JsonClassSerializable.register(B)


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()

JsonClassSerializable.register(A)

A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)

Edit

With some more of research I found a way to generalize without the need of the SUPERCLASS register method call, using a metaclass

import json
import collections

REGISTERED_CLASS = {}

class MetaSerializable(type):

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in REGISTERED_CLASS:
            REGISTERED_CLASS[cls.__name__] = cls
        return super(MetaSerializable, cls).__call__(*args, **kwargs)


class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()


A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s

回答 18

您可以使用

x = Map(json.loads(response))
x.__class__ = MyClass

哪里

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = Map(v)

        if kwargs:
            # for python 3 use kwargs.items()
            for k, v in kwargs.iteritems():
                self[k] = v
                if isinstance(v, dict):
                    self[k] = Map(v)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

一个通用的,面向未来的解决方案。

You can use

x = Map(json.loads(response))
x.__class__ = MyClass

where

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = Map(v)

        if kwargs:
            # for python 3 use kwargs.items()
            for k, v in kwargs.iteritems():
                self[k] = v
                if isinstance(v, dict):
                    self[k] = Map(v)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

For a generic, future-proof solution.


回答 19

使用json模块Python 2.6中的新增功能)或simplejson几乎始终安装的模块。

Use the json module (new in Python 2.6) or the simplejson module which is almost always installed.


Django开发IDE [关闭]

问题:Django开发IDE [关闭]

我已经做了一些Django开发,但是所有工作都在文本编辑器中进行。我很好奇其他人在Django开发中使用了哪些更高级的开发工具。

我习惯使用Visual Studio进行开发,并且真的很喜欢它提供的IntelliSense,代码完成和文件组织,并且希望找到可以在Django / Python环境中提供某些功能的工具(或工具组合)。

I have done a little Django development, but it has all been in a text editor. I was curious what more advanced development tools others are using in their Django development.

I am used to using Visual Studio for development and really like the IntelliSense, code completion, and file organization it provides and would like to find something (or a combination of tools) that would provide some of this in the Django/Python environment.


回答 0

我使用Eclipse和普通的PyDev。没有任何特定的Django功能。我想到的最好的方法是设置运行配置文件以运行开发Web服务器。

如果添加Web工具项目(WTP),则模板中的语法将突出显示,但是与特定模板语言无关的内容将不会突出显示。PyDev是一个不错的插件,如果您已经熟悉Eclipse并将其用于其他项目,则是一个不错的选择。

我记得NetBeans开始获得Python支持,但是我不知道现在在哪里。许多人对NetBeans 6赞不绝口,但是在Java世界中,Eclipse仍然是OSS IDE的王者。

I use Eclipse and a plain vanilla PyDev. There isn’t any specific Django functionality. The best I came up with was setting up a run profile to run the development web server.

If you add the web tools project (WTP), you’ll get syntax highlighting in your templates, but nothing that relates to the specific template language. PyDev is a decent plugin, and if you are already familiar with Eclipse and use it for other projects it is a good way to go.

I recall NetBeans starting to get Python support, but I have no idea where that is right now. Lots of people rave about NetBeans 6, but in the Java world Eclipse still reigns as the king of the OSS IDEs.


回答 1

PyCharm从支持Django和JetBrains的谷歌应用程序。看起来很有希望。

注意:如果要Django支持,则需要购买Professional版本的许可证。社区版本不支持Django。

There is PyCharm from JetBrains which supports Django and Google Apps. It looks promising.

Note: You need to buy a license for the Professional version if you want Django support. The Community version desn’t support Django.


回答 2

我使用Vim:

http://github.com/skyl/vim-config-python-ide

[更新]

Sublime Text 2非常棒。如果需要,它支持许多Vim命令:Vintage Mode

它有一个不错的包管理器:http : //wbond.net/sublime_packages/package_control

到目前为止,我使用这些软件包:

贾内罗

设置Django语法

CoffeeScript

崇高的林特

主题-苏打水

补充工具栏

我仍然喜欢Vim,但是…我是否提到Sublime Text插件是用Python编写的?

I use Vim:

http://github.com/skyl/vim-config-python-ide

[UPDATE]

Sublime Text 2 is pretty awesome. It supports a lot of Vim commands if you want them: Vintage Mode

It has a nice package manager: http://wbond.net/sublime_packages/package_control

I use these packages so far:

Djaneiro

SetDjangoSyntax

CoffeeScript

SublimeLinter

Theme – Soda

SideBarEnhancements

I still love Vim but … did I mention that Sublime Text plugins are written in Python?


回答 3

我使用Komodo Edit。签出打开Komodo编辑。

I use Komodo Edit. Check out the Open Komodo Edit.


回答 4


回答 5

我开始喜欢使用Aptana Studios + PyDev(和其他)插件进行各种Web应用程序开发。如您所知,它建立在强大的Eclipse之上,但是是专门为专注于Web应用程序开发而设计的。

I am beginning to enjoy working with Aptana Studios + PyDev (and other) plugins for all sorts of web application development. As you can tell, it is built on top of the powerful Eclipse, but is tailor-designed to focus on web application development.


回答 6

我的大多数开发工作都使用Kate(KDE高级文本编辑器),包括Django。它同时具有Python和Django模板语法高亮显示。当项目的很大一部分涉及HTML时,我将切换到Quanta +。

由于它使用了Kate的KPart,因此它对于编辑Python部件和HTML模板都同样有用,我拥有完整的Quanta +工具,同时还保留了Django特有的标记。

2013年更新:不幸的是,Quanta +已经死了很多年了,而且它永远也不会复活。另外,那里没有其他可用的HTML编辑器,所以现在一直都是Kate。

I use Kate (KDE Advanced Text Editor) for most of my development, including Django. It has both a Python and Django Templates syntax higlighting. I switch to Quanta+ when a significant part of the project involves HTML.

Since it uses Kate’s KPart, it’s just as good for editing the Python parts, and for the HTML templates i have the whole Quanta+ tools, while still highligting Django-specific tags.

Update 2013: Unfortunately, Quanta+ has been dead for years now, and there’s no hope that it will ever be resurrected. Also, there’s no other usable HTML editor out there, so it’s Kate all the time now.


回答 7

NetBeans for Python是我当前的最爱(比我发现的Eclipse更轻巧,更容易安装)。支持简单的重构,自动完成,错误/警告…

Eclipse Aptana PyDev可能是当今最完整的免费IDE之一(未经大量测试)

Wingware Python IDE是一种商业IDE,具有一些Django特定的项目设置功能,可以调试Django模板文件。

IntelliJ IDEA Ultimate Edition是另一个商业IDE,它也有正在大力开发的Python插件。我看到了一些演示,该演示在自动完成方面非常有前途(对于模板和Python)。

我仍然使用一个小的触摸修复程序的 Vim。另请参阅: Django的额外调整

NetBeans for Python is my current favorite (lighter and so much easier to install than Eclipse I found). Supports simple refactoring, autocompletion, errors/warnings…

Eclipse Aptana PyDev probably one of the most complete free IDE nowadays (haven’t tested a lot)

Wingware Python IDE a commercial IDE, which has some Django-specific project setup features the ability to debug Django template files.

IntelliJ IDEA Ultimate Edition another commercial IDE which has also a plugin for Python that is under heavy development. I saw some demo which look very promising on the auto-completion (for templates and Python).

Vim which I still use a small touch-fix application. See also: Extra tweaks for Django.


回答 8

PyCharm。到目前为止,我最好尝试过适用于Python,Django和Web开发的IDE。这是完全值得的钱。

PyCharm. It is best the IDE for Python,Django, and web development I’ve tried so far. It is totally worth the money.


回答 9

你们应该结帐PyCharm!这是第一个像样的Django IDE。

You guys should checkout PyCharm! It is the first decent Django IDE.


回答 10

Eclipse具有用于python开发的PyDev插件。不幸的是,我不确定它与Django集成得如何。

Eclipse has the PyDev plugin for python development. Unfortunately, I’m not sure how well it integrates with Django.


回答 11

据我所知,没有适用于Django的“ IDE”,但是有一些现成的支持Django的IDE,特别是模板的Django语法。

名字叫Komodo,它具有很多功能,但并不便宜。如果您不担心源代码控制或调试,那么有一个免费版本称为Komodo Edit

As far as I know there is not “an IDE” for Django, but there are some IDEs that support Django right out of the box, specifically the Django syntax for templates.

The name is Komodo, and it has a lot of features, but it’s not cheap. If you are not worried about source control or debugging then there is a free version called Komodo Edit.


回答 12

Visual Studio有一个实际的Python扩展:http : //pytools.codeplex.com/。真是太棒了。感觉就像我使用任何本地Visual Studio语言进行编码一样。该扩展甚至与Django兼容。最好的是:它是完全免费的。即使对于Visual Studio,它也只需要Visual Studio Shell即可运行,这是完全免费的。

There is an actual Python extension for Visual Studio: http://pytools.codeplex.com/. It’s absolutely fantastic. It feels the same as if I were coding in any native Visual Studio language. The extension is even compatabile with Django. And best of all: it’s totally free. Even for Visual Studio, it only requires the Visual Studio Shell to work, which is completely free.


回答 13

现在,您还可以使用Visual Studio2010。操作方法如下:

  • 下载并安装适用于Visual Studio的Python工具
  • 根据现有代码创建一个新项目(菜单文件新建来自现有代码的项目…
  • 指定您的Django项目文件夹并使用默认值。
  • 右键单击manage.py,然后选择设置为启动文件
  • 在项目属性的“ 调试”选项卡中,在“ 脚本参数 ”中添加“ runserver” 。
  • 您可以设置断点,并附加到Python进程进行调试。如果要调试而不必“附加到进程”,请在脚本参数中使用“ runserver –noreload”。但是,“-noreload”意味着您必须手动停止并重新启动Django开发Web服务器(以识别代码更改)。

如果您已经使用Visual Studio,这是一个很好的设置。

Python工具已更新。现在,它已经内置了对Django的支持。

Now you can also use Visual Studio 2010. Here’s how:

  • Download and install Python Tools for Visual Studio.
  • Create a new project from existing code (menu FileNewProject From Existing Code…)
  • Specify your Django project folder and use the defaults.
  • Right-click on manage.py and choose Set as Startup File.
  • In your project properties Debug tab, add “runserver” in Script Arguments.
  • You can set break points, and attach to the Python process for debugging. If you want to debug without having to “attach to process,” use “runserver –noreload” in your script arguments. However, the “–noreload” means you’ll have to stop and restart the Django development web server manually (to recognize your code changes).

This is a nice setup if you already use Visual Studio.

Python Tools has been updated. It has built in support for Django now.


回答 14

PyCharm,当然。我几乎全部尝试了它们,但是PyCharm是我发现对任何繁重开发工作最有用的工具。

简单来说,脚本一次使用的是我想到的任何内容(TextMate,Vim,Emacs,TextWrangler等,您都可以命名)。

PyCharm, definitely. I tried them all (almost), but PyCharm is the one I found most useful for any heavy development.

For simple, one time, scripts I use whatever comes to mind (TextMate, Vim, Emacs, TextWrangler, etc., you name it).


回答 15

我已经在PyDev和PyCharm中使用了Eclipse。PyCharm绝对是我尝试过的Django / Python的最佳IDE。它会为所有对象进行适当的模板突出显示和自动补全。它还可以进行跨文件引用。

它相当昂贵,但是绝对是我尝试过的最好的Django IDE。您可以在http://www.jetbrains.com/pycharm/download/上进行30天的评估。

I have used Eclipse with PyDev and PyCharm. PyCharm is definitely the best IDE for Django/Python I have tried. It does proper template highlighting and auto-completion for all objects. It also does cross-file referencing.

It’s quite expensive, but definitely the best Django IDE I have tried. You can try a 30 day evaluation at http://www.jetbrains.com/pycharm/download/.


回答 16

好吧,我一直在用自己的。最近,他们发布了Alpha版本。在这里是pfaide.com/

Well, I’ve been using my own one. Recently they released an alpha version. Here it is at pfaide.com/.


回答 17

我在Eclipse和Pydev上也取得了不错的成绩。尽管我仍然需要打开一个打开到项目目录的外壳来运行manage.py命令。我还一直将其与Bazaar插件一起用于版本控制和与服务器同步代码。

I’ve also had good results with Eclipse and Pydev. Although I still require a shell opened to the project directory to run manage.py commands. I’ve also been using it with the Bazaar plugin for revision control and syncing code with the server.


回答 18

我非常喜欢E Text Editor,因为它几乎是TextMate与Windows 的“连接” 。显然Django是基于Python的,对自动完成功能的支持是有限的(没有像intellisense这样的东西需要专门的IDE来了解每个库的复杂性),但是使用摘要和“单词完成”功能有很大帮助。此外,它还支持Django Python文件和模板文件以及CSS,HTML等。

我已经使用E Text Editor很长时间了,可以告诉您,在使用Django时,它胜过PyDev和Komodo Edit。对于其他类型的项目,PyDev和Komodo可能更合适。

I really like E Text Editor as it’s pretty much a “port” of TextMate to Windows. Obviously Django being based on Python, the support for auto-completion is limited (there’s nothing like intellisense that would require a dedicated IDE with knowledge of the intricacies of each library), but the use of snippets and “word-completion” helps a lot. Also, it has support for both Django Python files and the template files, and CSS, HTML, etc.

I’ve been using E Text Editor for a long time now, and I can tell you that it beats both PyDev and Komodo Edit hands down when it comes to working with Django. For other kinds of projects, PyDev and Komodo might be more adequate though.


回答 19

我也用凯特。凯特的简单性是其最大的特色。它不会妨碍您。(这当然是高度主观的意见。)

Kate包括一个Python代码浏览器插件。但这对IMO没有用。更改代码/视图时不会自动更新。另外,更新时,整个树都将折叠,并且您必须自己再次对其进行扩展。点击次数过多。

相反,我使用了Pâté随附的Source Browser插件。确实确实会导致Kate暂时冻结,但到目前为止没有崩溃或此类事件。

无耻的博客插件:有关将Django与Kate(Pâté)结合使用的更多信息

I use Kate as well. Kate’s simplicity is its biggest feature. It doesn’t get in your way. (This is of course highly subjective opinion.)

Kate includes a Python code browser plugin. But it isn’t useful IMO. No automatic updates when you change the code/view. Also when you update, the whole tree is collapsed, and you have to expand it again yourself. Too many clicks.

Instead, I use the Source Browser plugin that comes with Pâté. It does cause Kate to freeze temporarily sometimes, but no crashes or anything of that sort so far.

Shameless blog plug: more on using Django with Kate (Pâté)


回答 20

如果您喜欢Vim作为编辑器,那么这里有一些有关如何针对Django开发进行调优的建议(直至完整的IDE):http : //code.djangoproject.com/wiki/UsingVimWithDjango

If you like Vim as an editor, here are some suggestions on how to tune it (up to the point of a full fledged IDE) for Django development: http://code.djangoproject.com/wiki/UsingVimWithDjango.


回答 21

Editra支持Django模板语言语法突出显示。您可以将其配置为更好的记事本或基本的IDE。

Editra supports Django Template Language syntax highlighting. You can configure it either as a better Notepad or a basic IDE.


回答 22

我写了一篇有关NetBeans对Django的新的和即将推出的支持的博客文章。当与它已经非常出色的Python,JavaScript,HTML和CSS支持配合使用时,在我看来,它是一个不错的选择!

I made a blog post about NetBeans’ new and upcoming support for Django. When paired with its already fantastic Python, JavaScript, HTML and CSS support, it’s a strong candidate in my mind!


回答 23

安装了Django和django-html捆绑软件的TextMate为您提供语法突出显示和出色的可扩展性。它轻巧且使用有趣。

是指向Python的TextMate的代码完成项目的链接(我自己没有使用过)。至于“智能”(据我理解是内联文档参考),TextMate也有。

TextMate with the Django and django-html bundles installed gives you syntax highlighting and great extensibility. It is lightweight and fun to use.

Here is a link to a code completion project for TextMate with Python (which I haven’t used myself). As for “intellisense” (which I understand to be inline-doc reference), TextMate has that too.


回答 24

来自http://www.wingware.com的Wingware编辑器特定于Python,并且对Python / Django / Zope等具有很好的自动完成功能。

它具有一个内置的Python外壳程序来运行代码片段(或选择并运行),并支持Mercurial / Git等,以及一个内置的unittest / nose / doctest测试运行程序。虽然它是商业性的,但是因为它是用Python编写的,所以它是跨平台的。

我前一阵子买了它,以为它看起来很笨拙,但是我已经尝试了所有这些并继续回来。请注意,我是Windows专家,没有Emacs或Vim技能,因此无法利用它。Mac版本需要X Window,并且似乎更容易出错。

The Wingware editor from http://www.wingware.com is Python-specific with very good auto-completion for Python/Django/Zope, etc.

It has a built in Python shell to run snippets (or select and run) and support for Mercurial/Git, etc. and a built-in unittest/nose/doctest test runner. It’s commercial though, but as it is written in Python, it’s cross platform.

I bought it a while ago, and thought it looked dorky, but I’ve tried them all and keep coming back. Caveat that I am a Windows guy with no Emacs or Vim skills, so leveraging that was not an option. And the Mac version requires X Window and seems to be more glitchy.


回答 25

盖尼

它基于GTK2,快速,轻巧,可用于Linux和Windows。

Geany

It is GTK2 based, fast, lightweight, available for Linux and Windows.


回答 26


回答 27

Ulipad是个不错的选择。 http://code.google.com/p/ulipad/


回答 28

我一直使用Vim或Kate,但我更希望使用成熟的IDE。鉴于它不像Visual Studio那么重。

I have consistently used Vim or Kate, but I would prefer a full-blown IDE. Given it is not as heavy as Visual Studio.


回答 29

我自己喜欢Eclipse + PyDev和/或eric。PyDev的新版本提供了一些非常出色的代码完成支持。

由于我将Eclipse用于PyDev,因此我使用Platform Runtime Binary + PyDev + Subclipse的精简安装。

I like Eclipse + PyDev and/or eric, myself. The new version of PyDev has some pretty awesome code completion support.

Since I only use Eclipse for PyDev, I use a slim install of just the Platform Runtime Binary + PyDev + Subclipse.


Django auto_now和auto_now_add

问题:Django auto_now和auto_now_add

对于Django 1.1。

我的models.py中有这个:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

更新行时,我得到:

[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error]   return self.cursor.execute(query, args)

我数据库的相关部分是:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

这值得关注吗?

附带问题:在我的管理工具中,这两个字段没有显示。那是预期的吗?

For Django 1.1.

I have this in my models.py:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

When updating a row I get:

[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error]   return self.cursor.execute(query, args)

The relevant part of my database is:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

Is this cause for concern?

Side question: in my admin tool, those two fields aren’t showing up. Is that expected?


回答 0

auto_now设置了属性的任何字段也会继承editable=False,因此不会显示在管理面板中。过去有过关于使auto_nowand auto_now_add参数消失的讨论,尽管它们仍然存在,但我觉得您最好只使用自定义save()方法

因此,为了使其正常工作,我建议不要使用auto_nowauto_now_add而是定义自己的save()方法以确保created仅在id未设置的情况下(例如,首次创建该项目时)对其进行更新,并使其在modified每次该项目更新时进行更新已保存。

我已经使用Django编写的其他项目完成了完全相同的操作,因此您save()将看起来像这样:

from django.utils import timezone

class User(models.Model):
    created     = models.DateTimeField(editable=False)
    modified    = models.DateTimeField()

    def save(self, *args, **kwargs):
        ''' On save, update timestamps '''
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        return super(User, self).save(*args, **kwargs)

希望这可以帮助!

编辑以回应评论:

我坚持重载save()与依赖这些字段参数的原因有两个:

  1. 前述的起伏具有其可靠性。这些参数在很大程度上取决于Django知道如何与之交互的每种类型的数据库对待日期/时间戳字段的方式,并且似乎在每个发行版之间都会中断和/或更改。(我相信这是彻底删除它们的呼吁的推动力)。
  2. 它们仅在DateField,DateTimeField和TimeField上起作用,使用这种技术,您可以在每次保存项目时自动填充任何字段类型。
  3. 使用django.utils.timezone.now()vs. datetime.datetime.now(),因为它会根据来返回可感知TZ或天真的datetime.datetime对象settings.USE_TZ

为了解决OP为何看到该错误的原因,我不完全知道,但created尽管有,但看起来根本没有被填充auto_now_add=True。对我来说,它是一个bug,并且在我上面的小列表中强调了项目#1: auto_now并且auto_now_add充其量是片状的。

Any field with the auto_now attribute set will also inherit editable=False and therefore will not show up in the admin panel. There has been talk in the past about making the auto_now and auto_now_add arguments go away, and although they still exist, I feel you’re better off just using a custom save() method.

So, to make this work properly, I would recommend not using auto_now or auto_now_add and instead define your own save() method to make sure that created is only updated if id is not set (such as when the item is first created), and have it update modified every time the item is saved.

I have done the exact same thing with other projects I have written using Django, and so your save() would look like this:

from django.utils import timezone

class User(models.Model):
    created     = models.DateTimeField(editable=False)
    modified    = models.DateTimeField()

    def save(self, *args, **kwargs):
        ''' On save, update timestamps '''
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        return super(User, self).save(*args, **kwargs)

Hope this helps!

Edit in response to comments:

The reason why I just stick with overloading save() vs. relying on these field arguments is two-fold:

  1. The aforementioned ups and downs with their reliability. These arguments are heavily reliant on the way each type of database that Django knows how to interact with treats a date/time stamp field, and seems to break and/or change between every release. (Which I believe is the impetus behind the call to have them removed altogether).
  2. The fact that they only work on DateField, DateTimeField, and TimeField, and by using this technique you are able to automatically populate any field type every time an item is saved.
  3. Use django.utils.timezone.now() vs. datetime.datetime.now(), because it will return a TZ-aware or naive datetime.datetime object depending on settings.USE_TZ.

To address why the OP saw the error, I don’t know exactly, but it looks like created isn’t even being populated at all, despite having auto_now_add=True. To me it stands out as a bug, and underscores item #1 in my little list above: auto_now and auto_now_add are flaky at best.


回答 1

但是我想指出的是,已接受答案中表达的观点有些过时。根据最近的讨论(django bug #7634 12785),即使您进入原始讨论,auto_now和auto_now_add也不行。,您也会在自定义保存中找到针对RY的强大论点(如DRY)方法。

提供了一个更好的解决方案(自定义字段类型),但是没有获得足够的动力使其成为django。您可以三行编写自己的代码(这是Jacob Kaplan-Moss的建议)。

from django.db import models
from django.utils import timezone


class AutoDateTimeField(models.DateTimeField):
    def pre_save(self, model_instance, add):
        return timezone.now()

#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)

But I wanted to point out that the opinion expressed in the accepted answer is somewhat outdated. According to more recent discussions (django bugs #7634 and #12785), auto_now and auto_now_add are not going anywhere, and even if you go to the original discussion, you’ll find strong arguments against the RY (as in DRY) in custom save methods.

A better solution has been offered (custom field types), but didn’t gain enough momentum to make it into django. You can write your own in three lines (it’s Jacob Kaplan-Moss’ suggestion).

from django.db import models
from django.utils import timezone


class AutoDateTimeField(models.DateTimeField):
    def pre_save(self, model_instance, add):
        return timezone.now()

#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)

回答 2

谈论一个附带的问题:如果您想在admin中查看此字段(尽管您将无法对其进行编辑),则可以将其添加readonly_fields到admin类中。

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

好吧,这仅适用于最新的Django版本(我相信1.3及更高版本)

Talking about a side question: if you want to see this fields in admin (though, you won’t be able to edit it), you can add readonly_fields to your admin class.

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

Well, this applies only to latest Django versions (I believe, 1.3 and above)


回答 3

我认为这里最简单(也许也是最优雅)的解决方案是利用您可以设置default为可调用对象的事实。因此,要绕过管理员对auto_now的特殊处理,您可以像这样声明字段:

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)

重要的是不要使用timezone.now()默认值,因为默认值不会更新(即,仅在加载代码时设置默认值)。如果您发现自己经常这样做,则可以创建一个自定义字段。但是,我认为这已经很干燥了。

I think the easiest (and maybe most elegant) solution here is to leverage the fact that you can set default to a callable. So, to get around admin’s special handling of auto_now, you can just declare the field like so:

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)

It’s important that you don’t use timezone.now() as the default value wouldn’t update (i.e., default gets set only when the code is loaded). If you find yourself doing this a lot, you could create a custom field. However, this is pretty DRY already I think.


回答 4

如果您像这样更改模型类:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True

然后,该字段将显示在我的管理员更改页面中

If you alter your model class like this:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True

Then this field will show up in my admin change page


回答 5

根据我已经阅读的内容以及到目前为止的Django经验,auto_now_add确实存在问题。我同意詹森主义—覆盖干净的普通保存方法,您知道正在发生什么。现在,要使其干燥,请创建一个称为TimeStamped的抽象模型:

from django.utils import timezone

class TimeStamped(models.Model):
    creation_date = models.DateTimeField(editable=False)
    last_modified = models.DateTimeField(editable=False)

    def save(self, *args, **kwargs):
        if not self.creation_date:
            self.creation_date = timezone.now()

        self.last_modified = timezone.now()
        return super(TimeStamped, self).save(*args, **kwargs)

    class Meta:
        abstract = True

然后,当您想要一个具有这种耗时行为的模型时,只需子类化即可:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

如果您希望这些字段显示在admin中,则只需删除该editable=False选项

Based on what I’ve read and my experience with Django so far, auto_now_add is buggy. I agree with jthanism — override the normal save method it’s clean and you know what’s hapenning. Now, to make it dry, create an abstract model called TimeStamped:

from django.utils import timezone

class TimeStamped(models.Model):
    creation_date = models.DateTimeField(editable=False)
    last_modified = models.DateTimeField(editable=False)

    def save(self, *args, **kwargs):
        if not self.creation_date:
            self.creation_date = timezone.now()

        self.last_modified = timezone.now()
        return super(TimeStamped, self).save(*args, **kwargs)

    class Meta:
        abstract = True

And then, when you want a model that has this time-stampy behavior, just subclass:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

If you want the fields to show up in admin, then just remove the editable=False option


回答 6

这值得关注吗?

不,Django在保存模型时会自动为您添加它,因此是可以预期的。

附带问题:在我的管理工具中,这两个字段没有显示。那是预期的吗?

由于这些字段是自动添加的,因此不会显示。

正如synack所说的,除此以外,在django邮件列表上已经有辩论将其删除,因为它“设计得不好”并且是“黑客”。

与使用auto_now相比,在我的每个模型上编写自定义的save()要痛苦得多

显然,您不必将其写入每个模型。您可以将其写入一个模型并从中继承其他模型。

但是,因为auto_addauto_now_add在那里,我会用他们,而不是试图写一个方法我自己。

Is this cause for concern?

No, Django automatically adds it for you while saving the models, so, it is expected.

Side question: in my admin tool, those 2 fields aren’t showing up. Is that expected?

Since these fields are auto added, they are not shown.

To add to the above, as synack said, there has been a debate on the django mailing list to remove this, because, it is “not designed well” and is “a hack”

Writing a custom save() on each of my models is much more pain than using the auto_now

Obviously you don’t have to write it to every model. You can write it to one model and inherit others from it.

But, as auto_add and auto_now_add are there, I would use them rather than trying to write a method myself.


回答 7

今天我在工作中需要类似的东西。默认值为timezone.now(),但在继承自的管理视图和类视图中均可编辑FormMixin,因此对于在我中创建models.py的代码,以下代码满足了这些要求:

from __future__ import unicode_literals
import datetime

from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now

def get_timezone_aware_now_date():
    return localtime(now()).date()

class TestDate(models.Model):
    created = models.DateField(default=lazy(
        get_timezone_aware_now_date, datetime.date)()
    )

对于DateTimeField,我想.date()从功能中删除并更改datetime.datedatetime.datetime或更好timezone.datetime。我没有尝试过DateTime,只有尝试过Date

I needed something similar today at work. Default value to be timezone.now(), but editable both in admin and class views inheriting from FormMixin, so for created in my models.py the following code fulfilled those requirements:

from __future__ import unicode_literals
import datetime

from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now

def get_timezone_aware_now_date():
    return localtime(now()).date()

class TestDate(models.Model):
    created = models.DateField(default=lazy(
        get_timezone_aware_now_date, datetime.date)()
    )

For DateTimeField, I guess remove the .date() from the function and change datetime.date to datetime.datetime or better timezone.datetime. I haven’t tried it with DateTime, only with Date.


回答 8

您可以将其timezone.now()用于创建和auto_now修改:

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

如果您使用的是自定义主键而不是默认键auto- increment intauto_now_add将导致错误。

下面是Django默认的代码DateTimeField.pre_saveauto_nowauto_now_add

def pre_save(self, model_instance, add):
    if self.auto_now or (self.auto_now_add and add):
        value = timezone.now()
        setattr(model_instance, self.attname, value)
        return value
    else:
        return super(DateTimeField, self).pre_save(model_instance, add)

我不确定参数add是什么。我希望它会像:

add = True if getattr(model_instance, 'id') else False

新记录将没有attr id,因此getattr(model_instance, 'id')返回False将导致未在字段中设置任何值。

You can use timezone.now() for created and auto_now for modified:

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

If you are using a custom primary key instead of the default auto- increment int, auto_now_add will lead to a bug.

Here is the code of Django’s default DateTimeField.pre_save withauto_now and auto_now_add:

def pre_save(self, model_instance, add):
    if self.auto_now or (self.auto_now_add and add):
        value = timezone.now()
        setattr(model_instance, self.attname, value)
        return value
    else:
        return super(DateTimeField, self).pre_save(model_instance, add)

I am not sure what the parameter add is. I hope it will some thing like:

add = True if getattr(model_instance, 'id') else False

The new record will not have attr id, so getattr(model_instance, 'id') will return False will lead to not setting any value in the field.


回答 9

至于您的管理员显示,请参阅此答案

注意:auto_now并且默认auto_now_add设置为editable=False,这就是为什么这样。

As for your Admin display, see this answer.

Note: auto_now and auto_now_add are set to editable=False by default, which is why this applies.


回答 10

auto_now=True在Django 1.4.1中对我不起作用,但是以下代码救了我。用于时区感知日期时间。

from django.utils.timezone import get_current_timezone
from datetime import datetime

class EntryVote(models.Model):
    voted_on = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
        super(EntryVote, self).save(*args, **kwargs)

auto_now=True didn’t work for me in Django 1.4.1, but the below code saved me. It’s for timezone aware datetime.

from django.utils.timezone import get_current_timezone
from datetime import datetime

class EntryVote(models.Model):
    voted_on = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
        super(EntryVote, self).save(*args, **kwargs)

回答 11

class Feedback(models.Model):
   feedback = models.CharField(max_length=100)
   created = models.DateTimeField(auto_now_add=True)
   updated = models.DateTimeField(auto_now=True)

在这里,我们创建并更新了列,这些列在创建时以及有人修改反馈时都会带有时间戳。

auto_now_add将设置创建实例的时间,而auto_now将设置某人修改其反馈的时间。

class Feedback(models.Model):
   feedback = models.CharField(max_length=100)
   created = models.DateTimeField(auto_now_add=True)
   updated = models.DateTimeField(auto_now=True)

Here, we have created and updated columns that will have a timestamp when created, and when someone modified feedback.

auto_now_add will set time when an instance is created whereas auto_now will set time when someone modified his feedback.


回答 12

如果您使用的是南方,并且想要默认为将字段添加到数据库的日期,这就是答案:

选择选项2, 然后: datetime.datetime.now()

看起来像这样:

$ ./manage.py schemamigration myapp --auto
 ? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice: 2
 ? Please enter Python code for your one-off default value.
 ? The datetime module is available, so you can do e.g. datetime.date.today()
 >>> datetime.datetime.now()
 + Added field created_date on myapp.User

Here’s the answer if you’re using south and you want to default to the date you add the field to the database:

Choose option 2 then: datetime.datetime.now()

Looks like this:

$ ./manage.py schemamigration myapp --auto
 ? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice: 2
 ? Please enter Python code for your one-off default value.
 ? The datetime module is available, so you can do e.g. datetime.date.today()
 >>> datetime.datetime.now()
 + Added field created_date on myapp.User

Django Model()与Model.objects.create()

问题:Django Model()与Model.objects.create()

运行两个命令有什么区别:

foo = FooModel()

bar = BarModel.objects.create()

第二个方法是否立即BarModel在数据库中创建一个,而对于FooModelsave()必须显式调用该方法以将其添加到数据库中?

What it the difference between running two commands:

foo = FooModel()

and

bar = BarModel.objects.create()

Does the second one immediately create a BarModel in the database, while for FooModel, the save() method has to be called explicitly to add it to the database?


回答 0

https://docs.djangoproject.com/zh-CN/stable/topics/db/queries/#creating-objects

要在一个步骤中创建和保存对象,请使用create()方法。

https://docs.djangoproject.com/en/stable/topics/db/queries/#creating-objects

To create and save an object in a single step, use the create() method.


回答 1

两种语法不等效,并且可能导致意外错误。这是一个显示差异的简单示例。如果您有模型:

from django.db import models

class Test(models.Model):

    added = models.DateTimeField(auto_now_add=True)

然后创建第一个对象:

foo = Test.objects.create(pk=1)

然后尝试使用相同的主键创建一个对象:

foo_duplicate = Test.objects.create(pk=1)
# returns the error:
# django.db.utils.IntegrityError: (1062, "Duplicate entry '1' for key 'PRIMARY'")

foo_duplicate = Test(pk=1).save()
# returns the error:
# django.db.utils.IntegrityError: (1048, "Column 'added' cannot be null")

The two syntaxes are not equivalent and it can lead to unexpected errors. Here is a simple example showing the differences. If you have a model:

from django.db import models

class Test(models.Model):

    added = models.DateTimeField(auto_now_add=True)

And you create a first object:

foo = Test.objects.create(pk=1)

Then you try to create an object with the same primary key:

foo_duplicate = Test.objects.create(pk=1)
# returns the error:
# django.db.utils.IntegrityError: (1062, "Duplicate entry '1' for key 'PRIMARY'")

foo_duplicate = Test(pk=1).save()
# returns the error:
# django.db.utils.IntegrityError: (1048, "Column 'added' cannot be null")

回答 2

更新15.3.2017:

我已经对此打开了Django问题,似乎已经在这里被初步接受:https : //code.djangoproject.com/ticket/27825

我的经验是,当通过Django 在引用中使用ConstructorORM)类时,1.10.5数据中可能存在一些不一致(即,创建对象的属性可能获取输入数据的类型,而不是ORM对象属性的强制类型)。 :

models

class Payment(models.Model):
     amount_cash = models.DecimalField()

some_test.pyobject.create

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor.objects.create(**actual_data)
            print(type(_obj.amount_cash)) # Decimal
            assert created
           objs.append(_obj)
        return objs

some_test.pyConstructor()

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor(**actual_data)
            print(type(_obj.amount_cash)) # Float
            assert created
           objs.append(_obj)
        return objs

UPDATE 15.3.2017:

I have opened a Django-issue on this and it seems to be preliminary accepted here: https://code.djangoproject.com/ticket/27825

My experience is that when using the Constructor (ORM) class by references with Django 1.10.5 there might be some inconsistencies in the data (i.e. the attributes of the created object may get the type of the input data instead of the casted type of the ORM object property) example:

models

class Payment(models.Model):
     amount_cash = models.DecimalField()

some_test.pyobject.create

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor.objects.create(**actual_data)
            print(type(_obj.amount_cash)) # Decimal
            assert created
           objs.append(_obj)
        return objs

some_test.pyConstructor()

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor(**actual_data)
            print(type(_obj.amount_cash)) # Float
            assert created
           objs.append(_obj)
        return objs

如何在Django模型中删除记录?

问题:如何在Django模型中删除记录?

我要删除特定记录。如

delete from table_name where id = 1;

我怎么能做到这一点django model

I want to delete a particular record. Such as

delete from table_name where id = 1;

How can I do this in a django model?


回答 0

有两种方法:

要直接删除它:

SomeModel.objects.filter(id=id).delete()

要从实例中删除它:

instance = SomeModel.objects.get(id=id)
instance.delete()

There are a couple of ways:

To delete it directly:

SomeModel.objects.filter(id=id).delete()

To delete it from an instance:

instance = SomeModel.objects.get(id=id)
instance.delete()

回答 1

MyModel.objects.get(pk=1).delete()

如果具有指定主键的对象不存在,这将引发异常,因为它首先会尝试检索指定的对象。

MyModel.objects.filter(pk=1).delete()

如果具有指定主键的对象不存在,并且不会直接产生查询,则不会引发异常

DELETE FROM my_models where id=1
MyModel.objects.get(pk=1).delete()

this will raise exception if the object with specified primary key doesn’t exist because at first it tries to retrieve the specified object.

MyModel.objects.filter(pk=1).delete()

this wont raise exception if the object with specified primary key doesn’t exist and it directly produces the query

DELETE FROM my_models where id=1

回答 2

如果要删除一项

wishlist = Wishlist.objects.get(id = 20)
wishlist.delete()

例如,如果要删除收藏夹中的所有项目

Wishlist.objects.all().delete()

If you want to delete one item

wishlist = Wishlist.objects.get(id = 20)
wishlist.delete()

If you want to delete all items in Wishlist for example

Wishlist.objects.all().delete()

回答 3

如果要删除一个实例,请编写代码

delet= Account.objects.get(id= 5)
delet.delete()

如果要删除所有实例,请编写代码

delet= Account.objects.all()
delete.delete()

if you want to delete one instance then write the code

entry= Account.objects.get(id= 5)
entry.delete()

if you want to delete all instance then write the code

entries= Account.objects.all()
entries.delete()

回答 4

沃尔夫提供了一个很好的答案集中代码。让我在这里粘贴官方文档,以供大家参考。

Wolph provided a good answer focused codes. Let me just paste official doc here, for people’s reference.