gpt4 book ai didi

python - Django-根据查询集生成表单

转载 作者:太空狗 更新时间:2023-10-30 01:34:05 25 4
gpt4 key购买 nike

我有一个问题,我将问两种方式:简短和通用,因此以后的StackOverflow读者会受益,而冗长和详细,因此我可以完成工作而无需做任何事情。

简短和通用版本:

如何使Django生成类似表格的表格,其中表格中的某些信息来自数据库,而用户则填写其余信息?在提交表单时,表中的每一行都应成为数据库中的记录(当然,在经过验证之后)。

什么是最干净的方法?这种交互方式通常被称为什么?

示例表格

|=============================================================|
| QueriedValue | CalculatedValue | User_data | More_User_data |
|_____________________________________________________________|
| Foo 1 | Bar 1 | | |
| Foo 2 | Bar 2 | | |
| Foo 3 | Bar 3 | | |
... ... ... ... |
| Foo n | Bar n | | |
|=============================================================|

++++++++++
| Submit |
++++++++++

结果数据库记录
     TimeStamp + fk_Foo = natural primary key for this table
________________
/ \
|===========================================================|
| TimeStamp | fk_Foo | User_data | More_User_data |
|___________________________________________________________|
| submit_time | Foo 1 | Datum 1 | AnotherDatum 1 |
| submit_time | Foo 2 | Datum 2 | AnotherDatum 2 |
| submit_time | Foo 3 | Datum 3 | AnotherDatum 3 |
|... ... ... ... |
| submit_time | Foo n | Datum n | AnotherDatum n |
|===========================================================|

长版

我正在编写一个Web应用程序以跟踪我公司的气瓶使用情况。我们的建筑物中有一堆气体管道,我们需要知道什么时候哪个气瓶连接到了哪个气路。

我想要两种表格供技术人员填写:
  • 每日库存:每天早晨,储藏室的工作人员需要查看每条加气管线并记录管线的压力以及瓶的引用号。这会生成一堆4元组的记录(时间,行,瓶,psi);每天早晨,每条线一个。
  • 所需瓶更换:每天清点后,如果瓶快用完了,则需要进行更换,并且该变更需要记录下来。这应该为新瓶添加另一个条目到瓶表中,并为新连接添加另一个具有新(时间,管线,瓶,psi)信息的4元组。这种情况每周发生几次,但并非每天都有。

  • 因此,为了跟踪这一点,我正在编写一个Django应用程序。我有以下模型:
    # models.py
    class GasFarm(models.Model):
    """
    Represents a gas farm -- a collection of lines that are grouped together and managed as a unit.
    """
    name = models.CharField(max_length=30, unique=True)

    def __unicode__(self):
    return self.name

    class Bottle(models.Model):
    """
    Represents a gas bottle -- the physical cylinder -- that contains a mixture of gases.
    """

    # Options
    get_latest_by = 'date_added'

    # Fields
    BACKGROUND_TYPES = (
    ('h2/n2', "H2/N2"),
    ('h2/air', "H2/Air"),
    ('h2', "H2"),
    ('n2', "N2"),
    ('other', "Other"),
    )


    ppm = models.FloatField()
    mix = models.CharField(max_length=50, choices=BACKGROUND_TYPES, default='n2')
    ref = models.CharField(max_length=50, unique=True) # Every bottle has a unique ref or somebody fucked up.
    cert_date = models.DateTimeField()
    date_added = models.DateTimeField(default=timezone.now())

    def pct(self):
    return float(self.ppm)/10**4


    def __unicode__(self):
    return "{} ({}% {})".format(self.ref, self.pct(), self.mix,)


    class Line(models.Model):
    """
    Represents a gas line -- the physical plumbing -- that delivers gas from the bottles to the test stations.

    It is assumed that a gas line can have zero or one gas bottles attached to it at any given time. The Line model
    maps bottle objects and time-sensitive Reading objects to test stations.
    """

    # Fields
    gasfarm = models.ForeignKey(GasFarm)
    number = models.CharField(max_length=10, unique=True)
    bottles = models.ManyToManyField(Bottle, through='Reading')

    # Calculated fields. "current" is definitely not optional -- that's a super common query. The others? I'm not so
    # sure...
    def current(self):
    """
    Returns the most recently recorded Reading object associated with the line
    """
    return self.reading_set.latest(field_name='time')
    current.short_description = "latest reading"


    def last_checked(self):
    """
    Returns the date & time at which the most recent Reading object associated with this line was logged
    """
    return self.current().time
    last_checked.short_description = "last updated"

    def has_recent_reading(self):
    """
    Boolean flag for whether the reading is probably valid, or if someone needs to go out and take a new one.
    """
    latest_reading = self.current().time
    return timezone.now() - latest_reading < datetime.timedelta(days=3)
    has_recent_reading.boolean = True
    has_recent_reading.short_description = "Is data current?"


    def __unicode__(self):
    return self.number


    class Reading(models.Model):
    """
    A Reading links a Bottle to a Line at a given time, and provides a snapshot of the pressure at that time.
    """

    # Options
    get_latest_by = 'time'

    # Fields
    line = models.ForeignKey(Line)
    bottle = models.ForeignKey(Bottle)
    time = models.DateTimeField()
    psi = models.IntegerField(validators=[MaxValueValidator(2500)])

    def ref(self):
    """
    The reference number of the bottle listed in the reading
    """
    return self.bottle.ref
    def ppm(self):
    """
    The PPM concentration of the bottle listed in the reading
    """
    return self.bottle.ppm

    def pct(self):
    """
    The % concentration of the bottle listed in the reading
    """
    return self.bottle.pct()

    def mix(self):
    """
    The gas mix (e.g. H2/N2) of the associated bottle
    """
    return self.bottle.mix

    def __unicode__(self):
    # Example:
    # A0: 327.3 PPM H2/N2 2300 psi
    return "{}, {}: {} PPM {} {} psi".format(self.line, self.time, self.ppm(), self.mix(), self.psi)

    我已经使用一些脚本在数据库中填充了我们的积压数据,并且编写了一些 View 以将数据从数据库中提取出来。到目前为止,我对它们感到满意,并且结果看起来非常有希望-至少对于显示存储的数据而言。

    但是我不确定如何使用HTML表单干净地填充数据库。我希望表单基本上是两个单独的“工作表”,就像DMV给您的那种形式一样,并带有清晰明确的说明#justkidding。

    表格1:每日库存

    该表单将列出给定服务器场中的所有行,显示每行上应该是哪个瓶子(基于先前的读数/更新),然后提示用户输入一个值。这将要求技术人员每次提交表格时都要更新每条生产线上每个瓶子的压力-我们需要整个气体系统的全局快照。在理想情况下,该表格会将当前时间和每条线的最新压力读数预先填充到“读数时间”和“压力”字段中,以简化数据输入。
    # Cells with brackets [] are system-supplied, non-editable data displayed in the table. 
    # Cells without brackets are pre-filled with sensible defaults, but are user editable.
    | [Line] | [Current Bottle] | Reading Time | Pressure (psi) |
    ===============================================================
    | [A0] | [15-1478334] | 2014-7-14 9:34 | 2400 |
    | [A1] | [15-1458661] | 2014-7-14 9:34 | 500 |
    | [A2] | [15-4851148] | 2014-7-14 9:34 | 1850 |
    | [A3] | [15-1365195] | 2014-7-14 9:34 | 700 |
    ...
    ...
    | [C18] | [15-9555813] | 2014-7-14 9:34 | 2350 |
    |=====================================================================|

    在阅读完有关Forms,ModelForms和Formsets的Django文档后,我编写了一些代码来完成 几乎我想要的所有,但是Line和Bottle信息是可编辑的表单字段,因此我需要它们成为静态的路标填写表格的其余部分。但是,它们确实需要出现在生成的数据库记录中。

    我朦胧地意识到readonly和disable属性,以及当您想在表单中拥有只读内容时,似乎是从响应中的POST变量中清除数据的似乎很笨拙的解决方案,但是我仍然不清楚如何这些工作或为什么有必要。我想知道是否有一种更干净的方法来得到我想要的东西?也许带有以编程方式生成的标题或注释的表单?我真正想要的是:自动生成的表单填写指南。
    # Forms.py
    class PressureReadingUpdate(forms.ModelForm):
    class Meta:
    model = models.Reading

    PsiReadingFormset = formset_factory(PressureReadingUpdate, extra=0)

    # views.py
    def update_pressure(request):
    if request.method == 'POST':
    formset = forms.PsiReadingFormset(request.POST)
    if formset.is_valid():
    cd = formset.cleaned_data
    # do something? I'm not here yet...
    else:
    lines = models.Line.objects.all()
    now = datetime.datetime.now()
    initial = [{'line': l,
    'psi': l.current().psi,
    "bottle": l.current().bottle,
    'time': now} for l in lines]

    formset = forms.PsiReadingFormset(initial=initial,)
    return render(request, 'gas_tracking/gasfarm_form_psi_reading.html', {'formset': formset})

    表格2:更换储气瓶

    我想要一张所有气体管线的列表,其中包含当前的瓶子和压力(很容易,这是在其他地方完成的),然后是一个按钮,该按钮会弹出一个窗口,您可以在其中提交一个新的瓶子,就像您一样在管理界面中找到。如何制作弹出窗口?如何制作按钮?我什至不知道从哪里开始

    我对Django还是很陌生,已经搜寻了很多东西,但是没有找到任何可以回答我的问题的东西-也许我只是没有使用正确的关键字?

    谢谢你的帮助。

    -马特

    最佳答案

    使用FormSets动态生成表单

    所以我想出了这一点(经过大量的谷歌搜索,起誓和咬牙切齿)。马尔科姆·特雷丁尼克(Malcolm Tredinnick)在我的博客上写了我想做什么,这是一个善良的灵魂,保存在Django Snippets

    使用Malcom的代码作为模型,我解决了我的问题,就像一个迷人的

    class PressureReadingUpdate(forms.Form):
    """
    Form that asks for the pressure of a line given some attributes of that line.
    """
    psi = forms.IntegerField(widget=forms.NumberInput)

    def __init__(self, *args, **kwargs):
    self.line = kwargs.pop('line')
    kwargs['auto_id'] = "%s".format(self.line.number)
    super(PressureReadingUpdate, self).__init__(*args, **kwargs)

    class BasePsiReadingFormset(BaseFormSet):
    """
    Formset that constructs a group of PressureReadingUpdate forms by taking a queryset
    of Line objects and passing each one in turn to a PressureReadingUpdate form as it
    gets constructed
    """
    def __init__(self, *args, **kwargs):
    self.lines = kwargs.pop('lines')
    super(BasePsiReadingFormset, self).__init__(*args, **kwargs)
    self.extra = len(self.lines)
    self.max_num = len(self.lines)

    def _construct_form(self, i, **kwargs):
    kwargs['line'] = self.lines[i]
    form = super(BasePsiReadingFormset, self)._construct_form(i, **kwargs)
    return form

    PsiReadingFormset = formset_factory(form=PressureReadingUpdate, formset=BasePsiReadingFormset)

    这为您提供了一个带有额外kwarg的Formset,您可以将其传递给构造函数链。您可以在具有以下功能的 View (以及更典型的 initial= kwarg)中使用它:
    formset = PsiReadingFormset(lines=lines,
    initial=[{'psi': l.current().psi} for l in lines],

    因此,这是我能想到的最佳解释:

    传递给类似FormSet的任何kwargs(由 formset_factory函数使用非默认的'BaseFormSet'作为蓝图来完成)都会传递给 __init__方法,该传递方法是 BaseFormSet所基于的 FormSet的大部分(未更改)。

    这意味着您可以在 BaseFormSet.__init__中定义自定义行为,并且可以通过将运行时数据作为关键字参数传递给 BaseFormSet.__init__(在上面的示例中为 FormSet kwarg),将运行时数据中继到 lines=方法。我用它在 formset(基于'BasePsiReadingFormset'的 FormSet的实例)上设置属性。

    感到困惑了吗?起初我也是。

    但是真正的魔幻是了解 _construct_forms的工作原理:每次想要在集合中创建新的 FormSet时, Form都会调用此函数。它将任何无法识别的kwargs中继到应该管理一组的 Form的构造函数。

    因此,您只需要重载自定义 BaseFormSet._construct_forms即可包装父类(super class)的原始 _construct_forms并注入(inject)新的kwarg。然后,此kwarg将传递给自定义Form类的构造函数,并根据 Form的初始化函数对新的 Form实例进行整形。

    女士们,先生们,那就是您如何拥有一个FormSet的方法,该方法中包含许多相似定义但动态生成且略有不同的形式。

    了解了这一点之后,我可以看到它的优雅。但是,它使用了一些中级到高级的python,既不是立即显而易见的,也不是有据可查的。如果您像我一样在此方面挣扎,请随时向我发送消息。

    关于python - Django-根据查询集生成表单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24748883/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com