I am trying to build a media library for my personal website. I can upload files in the django admin just fine through a FileField but I want to be able to do in my media library page instead. When I try to validate the form I get from the media library page form.is_valid() fails and returns Not Found: /media/
My MEDIA_URL is media/
# views.py
class MediaView(UpdateView):
form_class = FileForm
model = File
template_name = 'myapp/media.html'
def get(self, request):
if (request.user.is_superuser):
form = self.form_class(None)
files = File.objects.all()
return render(request, self.template_name, {'form': form, 'files': files})
else:
return HttpResponseForbidden()
def post(self, request):
if (not request.user.is_superuser):
return HttpResponseForbidden()
form = self.form_class(request.POST)
if (form.is_valid()):
name = form.cleaned_data['name']
file = form.cleaned_data['file']
media = form.save(commit=False)
if (not name):
media.name = name
media.file = file
media = form.save()
return redirect('/media')
else:
return HttpResponseNotFound()
# models.py
class File(models.Model):
name = models.CharField(max_length=256, null=True, blank=True)
file = models.FileField()
date_uploaded = models.DateTimeField(default=datetime.now)
def __str__(self):
if (not self.name):
return str(self.file)
else:
return self.name
# (template) media.py
<div class="flex-container">
<div class="flex-column content">
<h1>Media</h1>
<form class="upload-form" role="form" method="post">
{% csrf_token %}
{{ form }}
<span class="upload-file-cover">Choose File:</span>
<button class="button" type="submit">Upload</button>
</form>
<ul class="media-uploads">
{% for file in files %}
<li>
<p class="clipboard-text">/{{ file.file.url }}</p>
<a class="copy-to-clipboard">
{{ file|filetypeicon }}
<div class="tooltip">Copy to clipboard</div>
</a>
{{ file }}
Delete
</li>
{% empty %}
Nothing here.
{% endfor %}
</ul>
</div>
{% include "personal/includes/sidebar-latest.html" %}
{% include "personal/includes/sidebar-profile.html" %}
# forms.py
from personal.models import File
from django import forms
class FileForm(forms.ModelForm):
class Meta:
model = File
fields = ['name', 'file']
When processing User uploaded file Django expecting another argument called request.FILES along with request.POST.
POST method
form = self.form_class(request.POST, request.FILES)
Related
Hi so I am new to Django and one of the things I'm trying to do is make a simple gallery application. Somehow I can't add images through the server via the forms if I use a Model Form although I can do it using a plain form. I've tried a lot of the stuff in here and also tried some Youtube stuff but it didn't still work.
Here is my models.py
from django.db import models
from django.urls import reverse
from django.core.validators import validate_image_file_extension
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/media')
class FavoriteImages(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(favorite=True)
# Create your models here.
class Photo(models.Model):
name = models.CharField(max_length=120, null=True)
photo = models.ImageField(storage=fs, upload_to='media/', validators=[validate_image_file_extension])
date_uploaded = models.DateTimeField(auto_now=True)
favorite = models.BooleanField(default=False, blank=False)
slug = models.SlugField(null=True, blank=True)
gallery = models.Manager()
gallery_favorites = FavoriteImages()
class Meta:
ordering = ['-date_uploaded']
My Views.py
from PIL import Image
def image_new(request, *args, **kwargs):
Image.init()
form = PhotoForm(data=request.POST, files=request.FILES)
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
redirect('../all')
context = {
'form': form
}
return render(request, "form.html", context)
My forms.py
class PhotoForm(forms.ModelForm):
name = forms.CharField(label='',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Title'}))
photo = forms.ImageField(widget=forms.FileInput(attrs={'class':'form-control'}))
favorite = forms.BooleanField(label='Mark as Favorite',widget=forms.CheckboxInput(attrs={'class':'form-check-input'}))
class Meta:
model = Photo
fields = ['name',
'photo',
'favorite']
my .html
{% extends "base.html" %}
{% block content %}
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post">
This form is a multipart.
{% else %}
<form method="post">
{% endif %}
{% csrf_token %}
{% if form.media %}
{{ form.media }}
{% endif %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Save"/>
</form>
{% endblock %}
I've placed this in the settings:
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Something I noticed:
The media folder (root) remains empty, but Model.photo has an url. (not null)
How do I modify my form so that the image gets posted?
EDIT: I fixed it by changing the widget of the ImageField. I don't know why it works now, but it does. Thanks for all the help
Why all those {% if %} in the template? I think it's unecessary. I would write it as follows,
{% extends "base.html" %}
{% block content %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary" value="Save"></button>
</form>
{% endblock %}
Your form can also be simplified as such,
class PhotoForm(forms.ModelForm):
name = forms.CharField(label='',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Title'}))
photo = forms.ImageField()
favorite = forms.BooleanField(label='Mark as Favorite',widget=forms.CheckboxInput(attrs={'class':'form-check-input'}))
'''
You don't need the Meta class when inheriting from forms.ModelForm. I think the widgets aren't necessary, unless you need to style with CSS specifics.
Your view can also be simplified quite a bit. You don't need PIL Image unless you are modifying your image.
I would write like this,
def image_new(request, *args, **kwargs):
form = PhotoForm()
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
redirect('../all')
context = {
'form': form
}
return render(request, "form.html", context)
Django will take care of saving the image to your media folder and assigning it to the ImageField in the model.
I want to show all posts of a specific user in their profile page but only their profile information is being displayed but not the posts.
My initial code was simply trying to loop through the posts in the template like this:
{% for post in user.posts.all %}
{% endfor %}
But it didn't work. So I tried this next.
views.py
user = get_object_or_404(User, username=username)
post = Post.objects.filter(created_by=user.id)
context = {
'user': user,
'post': post
}
return render(request, 'users/profile.html', context)
profile.html
<div class="user-profile">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
{{ user.username }}
{{ user.email }}
</div>
<div class="user-post-model">
{% for post in posts %}
<img class="rounded-circle post-img" src="{{ post.created_by.profile.image.url }}">
{{ post.title }}
{{ post.content|safe }}
{{post.tag}}
{{ post.created_by }}
{{ post.created_at|naturaltime}}
{% endfor %}
</div>
It didn't work either
I also tried using a ListView to post the posts. It worked but instead of showing the specific user's profile information it showed the logged in user's profile information but the specific user's posts.
like this:
def profile(request, username):
user = get_object_or_404(User, username=username)
context = {
'user': user
}
return render(request, 'users/profile.html', context)
class UserPostListView(ListView):
model = Post
template_name = 'users/profile.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(created_by=user).order_by('-created_at')
This is the post model
class Post(models.Model):
title = models.CharField(max_length=255)
content = RichTextField(blank=True, null=True, max_length=30000)
created_at = models.DateTimeField(default=timezone.now)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
tag = models.CharField(max_length=255, default='uncategorised')
class Meta:
ordering = ('-created_at',)
def __str__(self):
return self.title + ' | ' + str(self.created_by)
def get_absolute_url(self):
return reverse("post-detail", kwargs={"pk": self.pk})
What is wrong?
To loop over the related Post objects to User, use _set (Django Docs).
So in your case user.post_set:
{% for post in user.post_set.all %}
{{ post.title }}
{% endfor %}
I am creating a form using ModelForm to let the users upload a file along with a description . The is_valid() function isn't returning true and I am really confused. I have searched and there are many questions with same title as mine but they don't solve my problem.
here is forms.py:
class PostForm(forms.ModelForm):
document = forms.FileField(widget=forms.FileInput)
class Meta:
model = FeedModel
fields = ['description', 'document']
Here is models.py:
class FeedModel(models.Model):
description = models.CharField(max_length=255, blank=True)
document = models.FileField()
like = models.IntegerField(default=0)
dateTime = models.DateTimeField(auto_now=True, auto_created=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, default=0)
def get_absolute_url(self):
u=self.user
return reverse('home:feed',u.primary_key)
Here is views.py:
class PostView(CreateView):
form_class = PostForm
template_name = 'home/feedModel_form.html'
def get(self, request, *args, **kwargs):
form=self.form_class(None)
return render(request, self.template_name, {'form':form })
def post(self, request, *args, **kwargs):
logger = logging.getLogger(__name__)
form=self.form_class(request.POST)
if form.is_valid():
user=request.user
self.object=form.save(commit=False)
self.object.user=user
self.object.save()
logger.error("voila")
redirect({'home:feed'}, user.id)
return render(request, self.template_name, {'form':form })
def feedview(request, user_id):
user = User.objects.get(pk=user_id)
return render(request, 'home/feed.html', {'user': user})
Here is feedModel_form.html:
{% extends 'home/navbar.html' %}
{% block body %}
<div class="form">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'home/form.html' %}
<button type="submit" class="button button-block" `
name="reg_btn">Post</button>`
</form>
</div>
{% endblock %}
Here is form.html:
{% for field in form %}
<div class="field-wrap">
<label>
{{ field.label_tag }}<span class="req">*</span>
</label>
<div>{{ field }}</div>
</div>
{% endfor %}
To see why the form isn't valid, you should check form.errors.
One error will be because you have not passed request.FILES to the form.
form=self.form_class(request.POST, request.FILES)
There may be other errors as well. If you used {{ form }} in your template, Django would include the errors automatically. Since you are rendering the fields manually, it's up to you to include the errors.
The key problem here is that you have overridden post. That means that you're missing out on lots of the code from CreateView.
In your case, it looks like you could remove the post method, and simply override form_valid instead.
def form_valid(self, form):
self.object=form.save(commit=False)
self.object.user=user
self.object.save()
# Note that you had {'home:feed'} here which was incorrect
return redirect('home:feed', user_id)
Your document field expects an uploaded file and is required. In order for the form to actually get the file, you have to also pass it the uploaded file in views.py:
form = self.form_class(data=request.POST, files=request.FILES)
I need form where user can create article with several images. I use django-multiupload app for image field. I can select several images but when I try to submit the form I have message under the image field: "Field is empty and field is required". Where is my mistake? Why I have such message when image field is not empty?
Also maybe someone can advice good examples or apps to save several images. I would be very grateful for any help.
models.py:
class Article(models.Model):
description = models.TextField(_('Description'))
class Image(models.Model):
article= models.ForeignKey(Article, on_delete=models.CASCADE)
image = models.FileField(_('Image'), upload_to='images/%Y/%m/%d/')
forms.py:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('description', )
image = MultiFileField()
def save(self, commit=True):
instance = super(ArticleForm, self).save(commit)
for each in self.cleaned_data['image']:
Image.objects.create(image=each, article=instance)
return instance
views.py:
def article_add(request):
data = dict()
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
article = article_form.save(commit=False)
******
article.save()
data['form_is_valid'] = True
articles = Article.objects.all
context = {'articles': articles}
context.update(csrf(request))
data['html_article'] = render_to_string('project/article_list.html', context)
else:
data['form_is_valid'] = False
else:
article_form = ArticleForm()
context = {'article_form': article_form}
data['html_article_form'] = render_to_string('project/article_add.html', context, request=request)
return JsonResponse(data)
article_add.html:
{% load widget_tweaks %}
<form method="post" action="{% url 'article_add' %}" class="article-add-form">
{% csrf_token %}
{% for field in article_form %}
<div class="form-group{% if field.errors %} has-danger{% endif %}">
<label class="form-control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" %}
{% for error in field.errors %}
<div class="form-control-feedback">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Try adding "min_num" constraint on the image field,
image = MultiMediaField(min_num=1, media_type='image')
EDIT
def article_add(request):
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
article = article_form.save(commit=False)
******
article.save()
#assume you have already a view in the name 'article_list'.
return redirect('article_list')
else:
article_form = ArticleForm()
context = dict(article_form=article_form)
return render(request, 'project/article_add.html', context)
The problem maybe because you were trying to render two templates in a single view, also when using django template rendering render is a shortcut function, which is mostly preferred to use, rather than string converting and parsing into json.
Also, 'article_list' must be another view, which shows the list of all the articles, after adding a new article, you should consider redirecting to the list view. Here you were trying to render multiple templates, in a single view. You could make of something like this in your list view,
def article_list(request):
articles = Article.objects.all()
context = dict(articles=articles)
return render(request, 'project/article_list.html', context)
Although, these are my personal opinion regarding the code you just shared. Try this approach...
I am relatively new to python and Django.
The form is displaying itself, but when i choose a file and hit save, the form just refreshes itself and the selected file in the FormField disappears and the forms says that this filed is required.
The code is :
models.py
class ScribbleMedia(models.Model):
media = models.FileField(upload_to='photos')
def __unicode__(self):
return self.media
class Scribble(models.Model):
body = models.TextField()
user = models.ForeignKey(User)
media = models.ForeignKey(ScribbleMedia)
def __unicode__(self):
return u'%s, %s' % (self.user.username, self.media)
class Tag(models.Model):
name = models.CharField(max_length=64, unique= True)
scribbles = models.ManyToManyField(Scribble)
def __unicode__(self):
return self.name
views.py
def scribbler_save_page(request):
if request.method == 'POST':
form = ScribbleSaveForm(request.POST)
if form.is_valid():
#Create or get media.
media, dummy = ScribbleMedia.objects.get_or_create(
media=form.cleaned_data['media']
)
#Create or get media.
scribble, created=Scribble.objects.get_or_create(
user=request.user,
media=media
)
#update scribble body
scribble.body=form.cleaned_data['body']
# If the scibble is being updated, clear old tag list.
if not created:
scribble.tag_set.clear()
# Create new tag list.
tag_names = form.cleaned_data['tags'].split()
for tag_name in tag_names:
tag, dummy = Tag.objects.get_or_create(name=tag_name)
scribble.tag_set.add(tag)
# Save scribble to database.
scribble.save()
return HttpResponseRedirect ( '/user/%s/' % request.user.username
)
else:
form = ScribbleSaveForm()
variables = RequestContext (request, {
'form': form
})
return render_to_response ('scribble_save.html', variables)
forms.py
class ScribbleSaveForm(forms.Form):
media=forms.FileField(
label=u'add file',
widget=forms.FileInput()
)
text=forms.CharField(
label=u'description',
widget=forms.Textarea()
)
tags=forms.CharField(
label=u'Tags',
required=False,
widget=forms.TextInput(attrs={'size':64})
)
urls.py
(r'^save/$', scribbler_save_page),
scribble_save.html
{% extends "base.html" %}
{% block title %}Save Scribble {% endblock %}
{% block head %}Save Scribble { % endblock % }
{% block content %}
<form method="post" action=".">
{{ form.as_p }}
<input type="submit" value="save" />
{% csrf_token %}
</form>
{% endblock %}
Two things you need to do specially for file uploads:
(1) Your HTML form element must include the attribute enctype with the value multipart/form-data.
<form enctype="multipart/form-data" method="post" action=".">
(2) In your view, you'll find uploaded files in request.FILES instead of request.POST. To bind uploaded files to your form, pass in request.FILES as the second argument to the form constructor.
form = ScribbleSaveForm(request.POST, request.FILES)
Source: Django docs - The Forms API - Binding uploaded files to a form