在多个字段中使用UniqueConstraint时,如何在Django中捕获IntegrityError?

时间:2019-04-15 19:29:54

标签: django duplicates unique-constraint

我有一个模型,该模型的用户为1个字段(外键),另一个为字段skill_group。我需要确保用户没有添加重复的技能组,所以我添加了UniqueConstraint。这是由于/ skillgroup / create /下的IntegrityError导致系统错误而起作用 重复键值违反唯一约束“ unique_skillgroup”-如何捕获此异常并通知用户是否重复;否则保存吗?

Django / Python / Postgres的新手,我认为我可以通过覆盖save()函数来处理它,但是这是检查的一部分,无法访问用户,我已经读过,这里不应该处理。我应该使用尝试/保存捕获/消息吗?我已经尝试了一些没有运气的事情。我在这里看到过类似的问题,但并没有帮助。任何帮助表示赞赏。

models.py
class SkillGroup(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    skill_group = models.CharField(max_length=35)
    sequence = models.IntegerField(default=999)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['user', 'skill_group'], name='unique_skillgroup'),
        ]

    def __str__(self):
        return self.skill_group

    def get_absolute_url(self):
        return reverse('skillgroup-list')
views.py
class SkillGroupCreateView(LoginRequiredMixin, CreateView):
    model = SkillGroup
    fields = ['skill_group']

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.sequence = SkillGroup.objects.filter(user=self.request.user).order_by('sequence').last().sequence + 1
        return super().form_valid(form)
skillgroup_form.html
{% extends "recruiter/baseskills.html" %}
{% load crispy_forms_tags %}

{% block content%}
  <div class="content-section">
      <form method="post">
        {% csrf_token %}
        <fieldset class="form-group">
          <legend class="border-bottom mb-4">Skill Group</legend>
          {{ form|crispy }}
        </fieldset>
        <div class="form-group">
          <button class="btn btn-outline-info" type="submit">Add Skill Group</button>
        </div>
      </form>
  </div>
{% endblock content%}

我想捕获异常并保存记录(如果不是重复的话),或者在屏幕上显示“技能组已经存在”的消息,并将用户留在创建页面上。另外,如果这是最好的解决方案,我可以删除UniqueConstraint并使用代码进行处理。

2 个答案:

答案 0 :(得分:0)

您在这里无意间绕过了Django的表单验证,然后试图将无效输入保存到数据库,这就是Django从数据库反馈丑陋的IntegrityError而不是优雅地处理错误的原因。

如果您在表单中提交了重复的用户和技能组,则void printdata(uint32_t data) { Serial.printf("Printing unsigned integer %d", data); } // ints should be printed as unsigned ints? void printdata(int data) { printdata((uint32_t)data); //Call unsigned version } void printdata(float data) { Serial.printf("Printing float %i", data); } //doubles should be printed as floats void printdata(double data) { printdata((float)data); } 将有助于将错误消息返回到表单模板中:

  • “具有该用户和技能组的技能组已存在。”

但是,只有在表单中包含CreateView字段时,它才能执行此操作。我假设您已经排除了User来保持表单模板整洁,但这阻止了Django的表单验证检查组合是否已经存在。

要解决此问题,请将User添加到表单字段中作为隐藏的输入。我认为使用User的幕后魔术是不可能的,因此您需要创建一个CreateView来解决这个问题。

SkillGroupForm

# forms.py from django import forms from .models import SkillGroup class SkillGroupForm(forms.ModelForm): class Meta: model = SkillGroup fields = ('user', 'skill_group') widgets = { 'user': forms.HiddenInput, } # views.py from .forms import SkillGroupForm class SkillGroupCreateView(LoginRequiredMixin, CreateView): model = SkillGroup form_class = SkillGroupForm def get_initial(self): return {'user': self.request.user} def form_valid(self, form): form.instance.sequence = SkillGroup.objects.filter(user=self.request.user).order_by('sequence').last().sequence + 1 return super().form_valid(form) 方法将get_initial作为初始值传递到隐藏的表单字段中,因此不需要用户输入。

答案 1 :(得分:0)

尝试验证表单类中的 skill_group 字段。定义 clean_skill_group 方法like in docs。在那里,您可以获得与您的用户相关的所有SkillGroup对象的查询集(例如here),然后比较技能。但是您需要以某种方式将您的User对象或user_id(以获取User对象)推送到表单,然后再调用 form.is_valid()(或再调用一次 form.is_valid( ))。然后在您的html模板中显示表单错误。

class YourForm(forms.ModelForm):
    ....some fields, Meta....
    def clean_skill_group(self):
        your_user_object = ....
        previously_created_skills = your_user_object.skill_group_set.all()
        skill_input = self.cleaned_data["skill_group"]
        if skill_input in previously_created_skills:
            raise forms.ValidationError(("This skill group is already exist"), code="invalid") # I suppose you are using model form