Django在验证错误时以模型形式formset显示图像

时间:2013-03-04 08:14:27

标签: django django-models django-forms

我正在构建一个用户可以创建项目的应用程序;在每个项目中,他们可以创建“位置”,照片可以与之关联。 Photo是一种通用内容模型,可以与许多其他类型的模型相关联。在这种情况下,照片已经上传并与项目相关联。用户可以通过javascript模式窗口选择它们与位置相关联,然后向它们添加“注释”。

最初我手动完全构建了这个,并通过request.POST访问了视图中的照片。但是我对笔记中的文本值有些麻烦,所以决定更容易/更好地正确地做它并构建一个实际的模型formset。

通过访问表单的.instance属性,模板中会显示照片

这很好用 - 我可以添加照片和备注,如果之前已经添加过,也可以将它们从某个位置删除。 但问题是,如果表单有任何验证错误并且用户返回到模板来处理它们,则图像本身不会显示。这似乎是因为表单没有附加实例属性。通过标签<img src="{{MEDIA_URL}}{{ photo.instance.photo.small.url }}">(使用简单缩略图)在已保存到该位置的照片上显示照片时没有任何问题,但对于那些刚刚添加/编辑过的照片则不会。

我的问题是 - 在这种情况下如何展示漂亮的漂亮照片?

这是我的模特形式:

class PhotoDisplayForm(ModelForm):

class Meta:
    model = Photo
    fields = {'id', 'note', }
    widgets = {
        'note': Textarea(attrs={'rows': 3}),
        'photo': HiddenInput(),
        'id': HiddenInput(),
    }

views.py

def location_form(request, project_id, location_id=None):
project = Project.objects.get(id=project_id)
photos_formset = []

if location_id is None:  #creates unbound forms
    locPhotoFS = formset_factory(PhotoDisplayForm, max_num=0, can_delete=True)
    photos_formset = locPhotoFS(prefix='loc_photos')

    use_type = 'create' #this variable is passed to the template to display appropriate text
    location_form = QuadratForm(request.POST or None, project_id=project_id)

else:   ### edit an existing form  ####
    use_type = 'edit'
    location = get_object_or_404(Location, pk=location_id)
    location_form = QuadratForm(request.POST or None, project_id=project_id)

    location_photos = Photo.objects.filter(location = location)
    locPhotoFS = modelformset_factory(Photo, PhotoDisplayForm, max_num=0,  can_delete=True)
    photos_formset = locPhotoFS(queryset = location_photos, prefix='loc_photos')            

if request.method == 'POST': 
    photos_formset = locPhotoFS(request.POST, prefix='loc_photos')

    if location_form.is_valid() and photos_formset.is_valid():
        location_instance = location_form.save(commit=False)
        location_instance.project = get_object_or_404(Project, pk=project_id)
        location_instance.save()

        for form in photos_formset:
            p = form.cleaned_data['id']
            if form.cleaned_data['DELETE']:
                p.content_object = project #reassociate the photo with the project
                p.save()
            else:   
                p.content_object = location_instance
                p.note = form.cleaned_data['note'].strip()
                print p.note
                p.save()

        return HttpResponseRedirect(reverse('core.views.location', args=(project_id, location_instance.id)))
    else:
        print 'there are errors'
context = {'location_type': location_type, 'use_type': use_type,'location_form': location_form,'project_photos': project.photos.all()}
if photos_formset:
    context['photos_formset'] = photos_formset
return render(request, 'location_form.html', context)

模板

{% if photos_formset %}
    {% if location_form.non_field_errors %}{{ location_form.non_field_errors }}{% endif %}
    {{ photos_formset.management_form }} 
    {% for photo in photos_formset %}
    {% if photo.non_field_errors %}{{ photo.non_field_errors }}{% endif %}
    <div class="photo-details original" id="{{photo.instance.id}}_photo-details">
        <button type="button" photo="{{photo.instance.id}}" class="close pull-right photo-delete" aria-hidden="true">&times;</button>
        <div class="photo-notes">{{ photo.note.label_tag }}{{ photo.note|attr:"class:photo_notes_input" }}{{ photo.errors }}</div>
    {{ photo.id }}
    {{ photo.DELETE|attr:"class:delete-photo" }}
    <div class="thumbnail-caption">
    <img src="{{MEDIA_URL}}{{ photo.instance.photo.small.url }}">
    <br>{{ photo.instance.title }}
    </div>
    </div>
    {% endfor %}
    {% endif %}

这是照片模型本身,如果它有用:

class Photo(models.Model):

    title = models.CharField(max_length=30)
    photo = ThumbnailerImageField(upload_to=imagePath)
    object_id = models.PositiveIntegerField()
    content_type = models.ForeignKey(ContentType)
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    note = models.TextField(max_length=200, blank=True)

    def __unicode__(self):
        return self.title

1 个答案:

答案 0 :(得分:0)

我已经找到了一个(有点hacky)的解决方案。 因为我已经有一个名为project_photos的查询集被传递给模板(这会创建javascript模式列表,其中可以选择照片添加到该位置),我只需在模板中循环这些并找到id匹配的位置(变量照片是formset循环中的当前形式:

    <div class="thumbnail-caption">
        {% if photo.instance.title %}
        <img src="{{MEDIA_URL}}{{ photo.instance.photo.small.url }}">
        <br>{{ photo.instance.title }}
        {% else %}
            {% with p=photo.id.value %}
                {% for pp in project_photos %}
                    {% ifequal p|stringformat:"s" pp.id|stringformat:"s" %}
                        <img src="{{MEDIA_URL}}{{ pp.photo.small.url }}">
                        <br>{{ pp.title }}
                    {% endifequal %}
                {% endfor %}
            {% endwith %}
        {% endif %}
    </div>

有必要使用stringformat模板过滤器 - 这让我陷入了一段时间。

这不是一个完美的解决方案 - 因为那些照片仍然显示在原始的javascript窗口中(显然我可以解决这个问题) - 而且模板必须循环通过一个可能很慢的每个对象的潜在大对象列表事情发生了。

任何改善的建议都会感激不尽。