英文:
Django - Function Based View update record - error - Record Already exists
问题
MODELS.PY:
class Currency(models.Model):
    currency_code = models.CharField(max_length=3)
    currency_name = models.CharField(max_length=80)
    class Meta:
        verbose_name_plural = "货币"
    def __str__(self):
        return self.currency_code
class Forex(models.Model):
    currency_code = models.ForeignKey(Currency, on_delete=models.CASCADE)
    forex_rate = models.DecimalField(max_digits=8, decimal_places=4)
    last_updated_on = models.DateTimeField(auto_now=True)
    class Meta:
        verbose_name_plural = "外汇汇率"
    def __str__(self):
        return self.currency_code.currency_code
FORMS.PY:
class UpperCase(forms.CharField):
    def to_python(self, value):
        if value:
            return value.upper()
class CurrencyForm(forms.ModelForm):
    currency_code = UpperCase(
        label="货币代码",
        min_length=3,
        max_length=3,
        error_messages={"required": "货币代码不能为空"},
        required=True,
    )
    currency_name = UpperCase(
        label="货币名称",
        min_length=5,
        max_length=80,
        error_messages={"required": "货币名称不能为空"},
        required=True,
    )
    class Meta:
        model = Currency
        fields = ["currency_code", "currency_name"]
    # 检查重复的第二种方法正常工作
    def clean_currency_code(self):
        currency_code = self.cleaned_data.get("currency_code")
        if Currency.objects.filter(currency_code=currency_code).exists():
            raise forms.ValidationError(
                "货币代码 {} 已经存在!".format(currency_code)
            )
        return currency_code
VIEWS.PY:
def currency_edit(request, id):
    obj = Currency.objects.get(id=id)
    if request.method == 'POST':
        form = CurrencyForm(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()
            return redirect('currency_list_page')
    else:
        form = CurrencyForm(instance=obj)
    return render(request, 'main/currency_edit.html', locals())
URLS.PY:
urlpatterns = [
    path('currency_list/', views.currency_list, name='currency_list_page'),
    path('currency_add/', views.currency_add, name='currency_add_page'),
    path('currency_edit/<int:id>/', views.currency_edit, name='currency_edit_page'),
    path('currency_delete/<int:id>/', views.currency_delete, name='currency_delete_page'),
]
CURRENCY_EDIT.HTML:
<form action="{% url 'currency_edit_page' id %}" class="row g-3 mt-3" method="POST" novalidate>
    {% csrf_token %}
    <div class="row">
        {{id}}
        {{form.currency_code}}
        {{form.currency_name}}
    </div>
    <div class="col-12">
        <button class="btn btn-success" type="submit"><i class="fa-solid fa-floppy-disk"></i> 保存</button>
        <a class="btn btn-danger" href="{% url 'currency_list_page' %}">
            <i class="fa-solid fa-reply"></i> 取消</a>
    </div>
</form>
英文:
I am trying to update an existing record using Django 4.2, Crispy Forms, and Function Based View. The field currency_code in the table Currency is used as a ForeignKey in another table(s). I cannot update the currency record without getting the message that the record already exists. What am I doing wrong and where? Below is the code and screenshot:
MODELS.PY:
class Currency(models.Model):
    currency_code = models.CharField(max_length=3)
    currency_name = models.CharField(max_length=80)
    class Meta:
        verbose_name_plural = "Currencies"
    def __str__(self):
        return self.currency_code
class Forex(models.Model):
    currency_code = models.ForeignKey(Currency, on_delete=models.CASCADE)
    forex_rate = models.DecimalField(max_digits=8, decimal_places=4)
    last_updated_on = models.DateTimeField(auto_now=True)
    class Meta:
        verbose_name_plural = "Forex Rates"
    def __str__(self):
        return self.currency_code.currency_code
FORMS.PY:
class UpperCase(forms.CharField):
    def to_python(self, value):
        if value:
            return value.upper()
class CurrencyForm(forms.ModelForm):
    currency_code = UpperCase(
        label="Currency Code",
        min_length=3,
        max_length=3,
        error_messages={"required": "Currency Code Cannot Be Blank"},
        required=True,
    )
    currency_name = UpperCase(
        label="Currency Name",
        min_length=5,
        max_length=80,
        error_messages={"required": "Currency Name Cannot Be Blank"},
        required=True,
    )
    class Meta:
        model = Currency
        fields = ["currency_code", "currency_name"]
    # Method 2 for checking duplicates working ok
    def clean_currency_code(self):
        currency_code = self.cleaned_data.get("currency_code")
        if Currency.objects.filter(currency_code=currency_code).exists():
            raise forms.ValidationError(
                "Currency Code {} already exists!".format(currency_code)
            )
        return currency_code
VIEWS.PY:
def currency_edit(request, id):
    obj = Currency.objects.get(id=id)
    if request.method == 'POST':
        form = CurrencyForm(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()
            return redirect('currency_list_page')
    else:
        form = CurrencyForm(instance=obj)
    return render(request, 'main/currency_edit.html', locals())
URLS.PY:
urlpatterns = [
    path('currency_list/', views.currency_list, name='currency_list_page'),
    path('currency_add/', views.currency_add, name='currency_add_page'),
    path('currency_edit/<int:id>', views.currency_edit, name='currency_edit_page'),
    path('currency_delete/<int:id>', views.currency_delete, name='currency_delete_page'),
]
CURRENCY_EDIT.HTML
<form action="{% url 'currency_edit_page' id %}" class="row g-3 mt-3" method="POST" novalidate>
    {% csrf_token %}
    <div class="row">
        {{id}}
        {{form.currency_code}}
        {{form.currency_name}}
    </div>
    <div class="col-12">
        <button class="btn btn-success" type="submit"><i
                class="fa-solid fa-floppy-disk"></i> Save
        </button>
        <a class="btn btn-danger" href="{% url 'currency_list_page' %}">
            <i class="fa-solid fa-reply"></i> Cancel</a>
    </div>
</form>
答案1
得分: 1
抱歉,看起来我错过了你的表单clean_currency_code上的缩进(下次提问时要小心),这就是问题所在。
当您尝试编辑时,表单将引发您设置的验证。因为该函数将在.is_valid调用上运行,您还在编辑view上调用它,当然,如果您试图编辑第二个字段currency_name,那么code将不会更改,因此仍然存在。
如果currency_code字段应该是唯一的,请在您的模型上使用唯一字段选项,让框架为您工作:
models.py
class Currency(models.Model):
    currency_code = models.CharField(max_length=3, unique=True)
    currency_name = models.CharField(max_length=80)
forms.py
class UpperCase(forms.CharField):
    ...
class CurrencyForm(forms.ModelForm):
    class Meta:
        model = Currency
        fields = ["currency_code", "currency_name"]
    currency_code = UpperCase(
        label="Currency Code",
        min_length=3,
        max_length=3,
        error_messages={"required": "Currency Code Cannot Be Blank"},
        required=True,
    )
    currency_name = UpperCase(
        label="Currency Name",
        min_length=5,
        max_length=80,
        error_messages={"required": "Currency Name Cannot Be Blank"},
        required=True,
    )
英文:
Sorry, it seems that I missed indentation on your form clean_currency_code (careful with that next time asking a question), there is where the problem lies.
When you try to edit, form will raise the validation you set up. Because the function will run on .is_valid calls, which you also call on edit view and of course if you are trying to edit the second field currency_name then the code will not change and thus exists.
If currency_code field is supposed to be unique, then use the unique field option on your model and let the framework work for you:
models.py
class Currency(models.Model):
    currency_code = models.CharField(max_length=3, unique=True)
    currency_name = models.CharField(max_length=80)
forms.py
class UpperCase(forms.CharField):
    ...
class CurrencyForm(forms.ModelForm):
    class Meta:
        model = Currency
        fields = ["currency_code", "currency_name"]
    currency_code = UpperCase(
        label="Currency Code",
        min_length=3,
        max_length=3,
        error_messages={"required": "Currency Code Cannot Be Blank"},
        required=True,
    )
    currency_name = UpperCase(
        label="Currency Name",
        min_length=5,
        max_length=80,
        error_messages={"required": "Currency Name Cannot Be Blank"},
        required=True,
    )
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。




评论