最初在Django-Form中填充FileField

时间:2016-08-23 12:28:28

标签: python django django-class-based-views

我有一个描述网页的模型。 source_upload字段代表网页的屏幕截图。

为了将网站对象添加到我的应用程序,我使用基于django类的CreateView。这非常有效。

现在我尝试添加一种添加网站的半自动方式。您可以将URL传递给视图,并且视图会自动填充表单(并显示网页的屏幕截图)。用户应该能够查看所有自动提取的字段 - 尤其是自动生成的屏幕截图图像 - 更改它们并点击保存按钮将对象添加到数据库,将图像(如果已批准)添加到其最终位置。

我尝试在视图的get_initial方法中实现此功能。除了屏幕截图 - FileField之外,这非常有效。我在initial['source_upload']中设置的路径未显示在表单的FileInput小部件的current: <link>部分中。

如何为文件字段提供初始值?

models.py

class Site(models.Model):

    def get_source_upload_path(instance, filename):
        now = datetime.datetime.now()
        return "appname/sites/{}/{}/{}/site_{}_{}".format(now.year, now.month, now.day, instance.pk, filename)

    creationDate = models.DateTimeField(auto_now_add=True)
    last_modifiedDate = models.DateTimeField(auto_now=True)
    creator = models.ForeignKey('auth.User', related_name='siteCreated')
    last_modifier = models.ForeignKey('auth.User', related_name='siteLast_modified')

    date = models.DateTimeField(default=datetime.date.today)    
    title = models.CharField(max_length=240, blank=True)
    body = models.TextField(max_length=3000)

    source_url = models.URLField(blank=True)
    source_upload = models.FileField(upload_to=get_source_upload_path, blank=True)

    keywords = models.ManyToManyField("Keyword")

urls.py

url(r'site/add/$', views.SiteCreate.as_view(), name='site-add'),
url(r'site/add/(?P<source_url>[A-Za-z0-9\-._~:/\[\]@!$&\'\(\)\*\+,;=?#]+)/$', views.SiteCreate.as_view(), name='site-add-fromurl'),

forms.py

class SiteForm(ModelForm):
    class Meta:
        model = Site
        fields = ['date', 'title', 'body', 'source_url', 'source_upload', 'keywords']
        widgets = {
            'keywords' : CheckboxSelectMultiple(),
        }

views.py

class SiteCreate(LoginRequiredMixin, CreateView):
    model = Site
    template_name = 'appname/site_form.html'
    form_class = SiteForm
    success_url = reverse_lazy('appname:index')

    def form_valid(self, form):
        form.instance.creator = self.request.user
        form.instance.last_modifier = self.request.user
        return super(SiteCreate, self).form_valid(form)

    def get_initial(self):
        # Get the initial dictionary from the superclass method
        initial = super(SiteCreate, self).get_initial()

        try:
            #get target url from request
            fullpath = self.request.get_full_path()
            fullpath = fullpath.split("/")
            fullpath, querystring = fullpath[3:-1], fullpath[-1]
            source_domain = fullpath[2]
            fullpath = "/".join(fullpath)
            fullpath += querystring

            source_url = fullpath

            if (not source_url.startswith("http://") and not source_url.startswith("https://")):
                print("ERROR: url does not start with http:// or https://")
                return initial

            # ...
            # extract title, date & others with BeautifulSoup
            # ...

            #extract screenshot  (is there a better way?)
            from selenium import webdriver
            driver = webdriver.Firefox()
            driver.get(source_url)
            tmpfilename = "{}_{}.png".format(get_valid_filename(source_domain), get_valid_filename(title[:30]))
            now = datetime.datetime.now()
            tmpfilepath_rel =  "appname/sites/tmp/{}/{}/{}/{}".format(now.year, now.month, now.day, tmpfilename)
            tmpfilepath = settings.MEDIA_ROOT + tmpfilepath_rel

            folder=os.path.dirname(tmpfilepath)
            if not os.path.exists(folder):
                os.makedirs(folder)
            driver.save_screenshot(tmpfilepath)
            driver.quit()

            initial = initial.copy()
            initial['source_url'] = source_url
            initial['title'] = title
            initial['date'] = soup_date
            initial['body'] = body
            initial['source_upload'] = tmpfilepath_rel
        except KeyError as e:
            print("no valid source_url found. zeige also ganz normales add/new template")
        except IndexError as e:
            print("no valid source_url found. zeige also ganz normales add/new template")

        return initial

site_form.html (用于创建和更新视图)

{% extends "appname/base.html" %}
{% load staticfiles %}
{% block header %}
    <link rel="stylesheet" type="text/css" href="{% static 'appname/model_forms.css' %}" />
{% endblock %}
{% block body %}
        <form enctype="multipart/form-data" action="" method="post">{% csrf_token %}

            <div class="fieldWrapper">
                <div class="error">{{ form.date.errors }}</div>
                <div class="label">{{ form.date.label_tag }}</div>
                <div class="field">{{ form.date }}<br />{{ form.date.help_text }}</div>
                <div class="floatclear"></div>
            </div>
            <div class="fieldWrapper">
                <div class="error">{{ form.title.errors }}</div>
                <div class="label">{{ form.title.label_tag }}</div>
                <div class="field">{{ form.title }}<br />{{ form.title.help_text }}</div>
                <div class="floatclear"></div>
            </div>
            <div class="fieldWrapper">
                <div class="error">{{ form.body.errors }}</div>
                <div class="label">{{ form.body.label_tag }}</div>
                <div class="field">{{ form.body }}<br />{{ form.body.help_text }}</div>
                <div class="floatclear"></div>
            </div>
            <div class="fieldWrapper">
                <div class="error">{{ form.source_url.errors }}</div>
                <div class="label">{{ form.source_url.label_tag }}</div>
                <div class="field">{{ form.source_url }}<br />{{ form.source_url.help_text }}</div>
                <div class="floatclear"></div>
            </div>
            <div class="fieldWrapper">
                <div class="error">{{ form.source_upload.errors }}</div>
                <div class="label">{{ form.source_upload.label_tag }}</div>
                <div class="field">{{ form.source_upload }}<br />{{ form.source_upload.help_text }}</div>
                <div class="floatclear"></div>
            </div>
            <div class="fieldWrapper">
                <div class="error">{{ form.keywords.errors }}</div>
                <div class="label">{{ form.keywords.label_tag }}</div>

                <div class="field">
                    <ul class="checkbox-grid">
                        {% for kw in form.keywords %}
                            <li>
                                {{ kw.tag }}
                                <label for="{{ kw.id_for_label }}">
                                    {{ kw.choice_label }}
                                </label>
                            </li>
                        {% endfor %}
                    </ul>
                    <div class="checkbox_help_text"><br />{{ form.keywords.help_text }}</div>
                </div>
                <div class="floatclear"></div>
            </div>
            <input type="submit" value="Save" />
        </form>
        <div id="ObjectHistory">
            {% if site.pk %}
                <p>Created by: {{ site.creator }}</p>
                <p>Created on: {{ site.creationDate }}</p>
                <p>Last modified by: {{ site.last_modifier }}</p>
                <p>Last modified on: {{ site.last_modifiedDate }}</p>
                <p>Now: {% now "Y-m-d H:i:s" %} <a href="{% url 'appname:site-delete' site.pk %}"><button>delete</button></a></p>
            {% else %}
                <p>This is a new Site!</p>
                <p>Now: {% now "Y-m-d H:i:s" %}</p>
            {% endif %}
        </div>
{% endblock %}

1 个答案:

答案 0 :(得分:1)

这是因为表单使用的FileField的值不仅仅是文件的路径 - 它是FieldFile的一个实例(请参阅https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.fields.files.FieldFile)。

我不确定你是否可以直接实例化FieldFile,但至少你可以通过实例化模型来实现(你不需要保存它)。

在views.py中:

tmp_site = Site(source_upload=tmpfilepath_rel)
initial['source_upload'] = tmp_site.source_upload

或者,您可以在渲染html时手动添加文件链接:

<div class="currently"><a href="{{ form.source_upload.value }}">{{ form.source_upload.value }}</a></div>