Django update_or_create() 抛出“已存在”

问题描述 投票:0回答:1

这是我的 models.py。 请注意,对于容量模型:“年”、“月”、“员工”是唯一的。

class Employee(models.Model):
    last_name = models.CharField('Nachname', max_length=200)
    first_name = models.CharField('Vorname', max_length=200)
    address = models.CharField('Strasse und Hausnummer', max_length=200)
    zip_code = models.IntegerField('PLZ', default=4000)
    place = models.CharField('Wohnort', max_length=200)
    birth_date = models.DateField('Geburtsdatum')
    entry_date = models.DateField('Startdatum')
    email = models.CharField(max_length=200)
    phone = models.CharField(max_length=15, blank=True)
    ahv_no = models.CharField('AHV Nummer', max_length=200, blank=True)
    HEBAMME = 'HEBAMME'
    PFLEGEFACHFRAU = 'PFLEGEFACHFRAU'
    JOB_CHOICES = [
        (HEBAMME, 'Hebamme'),
        (PFLEGEFACHFRAU, 'Pflegefachfrau'),
    ]
    job = models.CharField(
        max_length=20,
        choices=JOB_CHOICES,
        default=HEBAMME,
    )
    workload = models.IntegerField('Pensum', validators=[MaxValueValidator(100)], default=100)
    active = models.BooleanField(default=True)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        on_delete=models.CASCADE
        )    
    services = models.ManyToManyField(
        'customers.Service',
        default=1,
        blank=True
        )
    def __str__(self):
        return self.first_name + ' ' + self.last_name

class Capacity(models.Model):
    employee = models.ForeignKey(
        Employee,
        blank=True,
        null=True,
        on_delete=models.CASCADE
    )
    number_wobe = models.IntegerField(
        'Anzahl Wochenbett',
        default=0
    )
    year_choices = (
        (2023, '2023'),
        (2024, '2024'),
        (2025, '2025'),
    )
    month_choices = (
        (1, 'Januar'),
        (2, 'Februar'),
        (3, 'März'),
        (4, 'April'),
        (5, 'Mai'),
        (6, 'Juni'),
        (7, 'Juli'),
        (8, 'August'),
        (9, 'September'),
        (10, 'Oktober'),
        (11, 'November'),
        (12, 'Dezember'),
    )
    year = models.IntegerField(
        'Jahr',
        choices=year_choices)
    month = models.IntegerField(
        'Monat',
        choices=month_choices)

    class Meta:
        unique_together = ('year', 'month', 'employee')

views.py 请注意,我的过滤器 kwargs 是年、月和员工(unique_together),要更新的项目是“number_wobe”。

def UpdateCreateCapacityView(request, year, month, employee_id):
    if request.method == 'POST':
        next = request.POST.get('next', '/')
        form = CapacityForm(request.POST)
        if form.is_valid():
            capacity = form['number_wobe'].value()
            cap, created = Capacity.objects.update_or_create(
                year=year,
                month=month,
                employee=employee_id,
                defaults={"number_wobe": capacity},
            )

            return HttpResponseRedirect(next)

    else:
        capacity = Capacity.objects.filter(year=year).filter(month=month).filter(employee=employee_id).values_list('number_wobe', flat=True)
        for item in capacity:
            capacity = item
        form = CapacityForm(initial={'employee': employee_id, 'year': year, 'month': month, 'number_wobe': capacity})

    return render(request,
                'planning/add_capacity.html',
                {'form': form})

add_capacity.html

{% extends 'dashboard/base.html' %}
{% load crispy_forms_tags %}

{% block title %}Kapazität verwalten{% endblock %}

{% block content %}
{% if user.is_authenticated %}

 <h1 class="title is-2">Kapazität verwalten</h1>

<form action="" method="post" novalidate>
  {% csrf_token %}
  {{ form|crispy }}
  <input type="hidden" name="next" value="{{ request.path }}">
  <input type="submit" value="Submit">
</form>

{% else %}
<h3 class="title is-3 has-text-centered has-text-weight-light">Du musst eingeloggt sein, um diese Seite zu sehen:</h3>
<br>
<div class="columns is-centered">
  <div class="colum is-half">
    <a class="button has-text-centered is-rounded is-primary is-large" href="{% url 'accounts:login' %}">Login</a>
  </div>
</div>
{% endif %}

{% endblock %}

问题: 提交表单时,我收到“已存在”错误。当使用 update_or_create() 时,我希望它只是更新对象(如果存在)。我(想我)已经根据 Django 文档使用了该函数(https://docs.djangoproject.com/en/4.2/ref/models/querysets/#update-or-create)。

python django model
1个回答
0
投票

虽然您没有显示您的

CapacityForm
,但我相信
ModelForm
是您问题的根源,而不是您对
update_or_create()
的使用。

当您调用

.is_valid()
ModelForm
方法时,它首先会验证您定义的表单,然后 然后 它会验证模型本身,如 Django 文档 中所述。这意味着,如果您提交具有现有年份、月份和员工的表单,则
is_valid()
函数将在到达 update_or_create() 方法之前抛出“
已经存在
”错误。您可以尝试使用断点进行验证,以显示表单在
is_valid()
调用后已经失效,或者在条件条件中使用打印语句来显示它从未被输入过。

我知道的两种处理方法是:

  1. 我链接的 Django 文档告诉你:

ModelForm.clean() 方法设置一个标志,使模型 验证步骤验证模型字段的唯一性 标记为 unique、unique_together 或 unique_for_date|month|year。

如果您想重写 clean() 方法并维护它 验证时,必须调用父类的clean()方法。

您可以查看 BaseModelForm 定义来亲自查看这个干净的方法,但您可以在您的CapacityForm中重写它,例如:

def clean(self):
    self._validate_unique = False
    return self.cleaned_data

这将跳过

is_valid()
调用的唯一验证,但请考虑在代码的其余部分中这是否可能导致使用表单的错误,并且它应该检查唯一性。

  1. 一些 stackoverflow 答案建议将其简单地更改为标准
    forms.Form
    而不是
    ModelForm
    。然后它不会执行模型验证步骤。如果您的模型上有很多约束和验证逻辑,那么您也必须在表单字段上重复,这可能会很烦人。
© www.soinside.com 2019 - 2024. All rights reserved.