2

I got a form as following:

class CourseAddForm(forms.ModelForm):
  """Add a new course"""
  name = forms.CharField(label=_("Course Name"), max_length=100)
  description = forms.Textarea()
  course_no = forms.CharField(label=_("course Number"), max_length=15)


  #Attach a form helper to this class
  helper = FormHelper()
  helper.form_id = "addcourse"
  helper.form_class = "course"

  #Add in a submit and reset button
  submit = Submit("Add", "Add New Record")
  helper.add_input(submit)
  reset = Reset("Reset", "Reset")
  helper.add_input(reset)

def clean(self):
  """ 
  Override the default clean method to check whether this course has been already inputted.
  """    
  cleaned_data = self.cleaned_data
  name = cleaned_data.get('name')
  hic = cleaned_data.get('course_no')

  try:
    course=Course.objects.get(name=name)
  except Course.DoesNotExist:
    course=None

  if course:
    msg = u"Course name: %s has already exist." % name
    self._errors['name'] = self.error_class([msg])
    del cleaned_data['name']
    return cleaned_data
  else:
    return self.cleaned_data

  class Meta:
    model = Course

As you can see I overwrote the clean method to check whether this course has already existed in the database when the user is trying to add it. This works fine for me.

However, when I want to add the same check for the form for editing, the problem happened. Because it is editing, so the record with same course name has already exist in the DB. Thus, the same check would throw error the course name has already exist. But I need to check the duplication in order to avoid the user updating the course name to another already existed course name.

I am thinking of checking the value of the course name to see if it is changed. If it has been changed, than I can do the same check as above. If it has not been changed, I don't need to do the check. But I don't know how can I obtain the origin data for editing.

Does anyone know how to do this in Django?

My view looks as following:

@login_required
@csrf_protect
@never_cache
@custom_permission_required('records.change_course', 'course')
def edit_course(request,course_id):
  # See if the family exists:
try:
  course = Course.objects.get(id=course_id)
except Course.DoesNotExist:
  course = None

if course:
  if request.method == 'GET':
    form = CourseEditForm(instance=course)
    return render_to_response('records/add.html',
                            {'form': form},
                            context_instance=RequestContext(request)
                            )
  elif request.method == 'POST':
    form = CourseEditForm(request.POST, instance=course)
    if form.is_valid():
      form.save()
      return HttpResponseRedirect('/records/')
    # form is not valid: 
    else:
      error_message = "Please correct all values marked in red."
      return render_to_response('records/edit.html', 
                              {'form': form, 'error_message': error_message},
                              context_instance=RequestContext(request)
                              )      
else:
  error = "Course %s does not exist. Press the 'BACK' button on your browser." % (course)
  return HttpResponseRedirect(reverse('DigitalRecords.views.error', args=(error,)))

Thank you.

4

2 回答 2

5

I think you should just set unique=True on the Course.name field and let the framework handle that validation for you.

Update:

Since unique=True is not the right answer for your case, you can check this way:

def clean(self):
    """ 
    Override the default clean method to check whether this course has
    been already inputted.
    """    
    cleaned_data = self.cleaned_data
    name = cleaned_data.get('name')

    matching_courses = Course.objects.filter(name=name)
    if self.instance:
        matching_courses = matching_courses.exclude(pk=self.instance.pk)
    if matching_courses.exists():
        msg = u"Course name: %s has already exist." % name
        raise ValidationError(msg)
    else:
        return self.cleaned_data

class Meta:
    model = Course

As a side note, I've also changed your custom error handling to use a more standard ValidationError.

于 2012-10-18T18:26:04.300 回答
3

I believe excluding the current instance id from the results would solve the problem:

from django.db.models import Q
try:
    qs = Course.objects.filter(name=self.cleaned_data.get('name'))
    if self.instance.pk is not None:
        qs = qs.filter(~Q(pk=self.instance.pk))
    course = qs.get()
except Course.DoesNotExist:
    course = None

However as dokkaebi pointed out, unique is really the better way to go with this, as this solution is vulnerable to race conditions. I'm not sure what your datamodel looks like but I suspect defining

class Meta:
    unique_together = ('department', 'name')

should accomplish what you want.

于 2012-10-18T18:40:37.153 回答