I'm trying to upload an image from a form and display it on another template, however the images in media cannot be found.
Model
class Upload(models.Model):
image = models.ImageField(upload_to='images/')
View
class HomePageView(CreateView):
model = Upload
form_class = UploadForm
template_name = 'home.html'
success_url = reverse_lazy('rank')
class RankView(ListView):
model = Rank
template_name = 'rank.html'
Settings
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATIC_URL = '/static/'
Urls
urlpatterns = [
path('', include('avatars.urls')),
path('admin/', admin.site.urls),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Upload Template
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload Image</button>
</form>
Retrieving Image with
<img src="{{upload.image.url}}">
Form
class UploadForm(forms.ModelForm):
class Meta:
model = Upload
fields = ['image']
Inspecting the code in browser, the img url is 'unknown'
Edit: tried changing my view, it made no difference
def upload_file(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/rank')
else:
form = UploadForm()
return render(request, 'home.html', {'form': form})
try using render insted of template_name and form does not have action attribute
return render(request, self.template_name, {'form': form})
[https://docs.djangoproject.com/en/2.2/topics/class-based-views/intro/#handling-forms-with-class-based-views][1]
i think in the you should remove the model from the create view because you are using the form_class , try it
class HomePageView(CreateView):
form_class = UploadForm
template_name = 'home.html'
success_url = reverse_lazy('rank')
hope it works
Related
I want to let the user upload multiple images per post. Similarly to an e-commerce platform with multiple images per product. But till now the images are not sent to the database.
That's my code so far:
models.py:
class Project(models.Model):
title = models.CharField(max_length=200)
describtion = models.TextField(null=True, blank=True)
class ProjectImage(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
image = models.FileField(upload_to="products")
forms.py:
class ProjectForm(ModelForm):
image = forms.ImageField(widget=ClearableFileInput(attrs={'multiple':True}))
class Meta:
model = Project
fields = ['title', 'describtion']
views.py:
def createProject(request):
form = ProjectForm()
if request.method == 'POST':
form = ProjectForm(request.POST)
images = request.FILES.getlist('image')
if form.is_valid():
project = form.save()
for i in images:
ProjectImage(project=project, image=i).save()
context = {'form':form}
return render(request, 'projects/project_form.html', context)
project_form.html:
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#text">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
<input type="submit" name="" id="">
</form>
settings.py:
STATIC_URL = '/static/'
MEDIA_URL = '/images/'
STATICFILES_DIRS = [
BASE_DIR / 'static'
]
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images')
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
project urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('projects.urls')),
]
app urls.py
urlpatterns = [
path("", views.createProject, name="create-project")
]
Issue:
You have made ProjectForm which relates to Project model, but the image field is in ProjectImage model. So, image field is not even passing to the template and you also haven't passed it in fields=['title','describtion'] in ProjectFrom.
You haven't made configurations for saving the media files in project's urls.py.
Solution:
You should make two forms in forms.py, first ProjectForm which will get the data for Project model and second ProjectImageForm which will get the list of images, then using request.FILES.getlist('image') you can save images which relates to a particular instance one by one in loop as you tried to save.
You should make media configurations in project's urls.py
Try Below Code:
forms.py
from django import forms
from django.forms import ClearableFileInput
from .models import Project, ProjectImage
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['title', 'describtion']
class ProjectImageForm(forms.ModelForm):
class Meta:
model = ProjectImage
fields = ['image']
widgets = {
'image': ClearableFileInput(attrs={'multiple': True}),
}
views.py
from django.http import HttpResponse
from django.shortcuts import redirect, render
from .forms import ProjectImageForm, ProjectForm
from .models import Project, ProjectImage
def createProject(request):
form = ProjectForm()
form2 = ProjectImageForm()
if request.method == 'POST':
form = ProjectForm(request.POST)
form2 = ProjectImageForm(request.POST, request.FILES)
images = request.FILES.getlist('image')
if form.is_valid() and form2.is_valid():
title = form.cleaned_data['title']
describ = form.cleaned_data['describtion']
print(title, describ)
project_instance = Project.objects.create(
title=title, describtion=describ)
print('-------------------------------------------')
print(project_instance)
print('-------------------------------------------')
for i in images:
ProjectImage.objects.create(project=project_instance, image=i)
return redirect('thanks')
context = {'form': form, 'form2': form2}
return render(request, 'projects/project_form.html', context)
def thanks(request):
return HttpResponse('<h1>Form saved.</h1>')
project_form.html or template file:
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.title.label_tag}}
{{form.title}}
<br><br>
{{form.describtion.label_tag}}
{{form.describtion}}
<br><br>
{{form2.image.label_tag}}
{{form2.image}}
<br><br>
<input type="submit" name="" id="">
</form>
project's urls.py
from django.conf.urls.static import static
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('projects.urls'))
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
app's urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls'))
]
Your models.py and settings.py can be remain same, but it's recommended to use
MEDIA_URL = 'media/' and MEDIA_ROOT = os.path.join(BASE_DIR, 'media/'), then you should make nested folders inside it to save images or any files.
Note: You should always return HttpResponseRedirect after dealing with POST data, the tip is not specific to Django, it's a good practice in general as stated in the tutorial4.
Note: Function based views are generally written in snake_case not camelCase, you may change it to create_project from createProject.
Note: Add / at the end of upload_to as upload_to='products/' in FileField in ProjectImage model.
I have one model Detail which has OneToOne relation with default User Model. I have a field FileField in my Detail model, where I want to upload the files using forms from frontend/templates.
I have been working around it a lot but I am not getting it done. I need help, please.
My models.py is:
from django.db import models
from django.contrib.auth.models import User
class Detail(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
file = models.FileField(verbose_name="CSV File", upload_to='csv_files/')
file_desc = models.TextField("CSV File Description")
def __str__(self):
return ("{} ({} {})".format(self.user.email, self.user.first_name, self.user.last_name))
My forms.py is:
from django.forms import ModelForm
from .models import Detail
class DetailForm(ModelForm):
class Meta:
model = Detail
fields = ['file', 'file_desc']
My views.py is:
from django.views import View
class UserAPI(View):
template_name = 'accounts/user.html'
def get(self, request):
form = DetailForm(instance=request.user)
context = {'form': form}
return render(request, self.template_name, context)
def post(self, request):
form = DetailForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
form.save()
return redirect('user')
context = {'form': form}
return render(request, self.template_name, context)
and my user.html (template) is:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
Every time I go to localhost:8000/user and fill the form and click on Submit Button, it gives me following error on frontend:
No File Chosen
and also the following statement appears above the File Selection Button:
This field is required.
I shall appreciate for the help. Thanks
UPDATE:
urls.py
urlpatterns = [
path('register', RegisterAPIHTML.as_view(), name='register'),
path('login', LoginAPIHTML.as_view(), name='login'),
path('user', UserAPI.as_view(), name='user'),
path('logout', LogoutAPI.as_view(), name='logout'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
MEDIA_ROOT = os.path.join(BASE_DIR, 'data/')
MEDIA_URL = '/media/'
I think your form is not saving anything because the line with form.is_valid() returns False.
You won't see anything in the form.errors because that attribute only shows field errors, not non-field errors. The non-field error in this case is that the required field user is missing. This is because you haven't specified it in the model form (also why it gets placed as a non-field error).
Also, you're not rendering the non-field errors in the HTML which is why you don't see them in your page after the post submission. I suggest using crispy-forms package (here's the link). It handles things like form rendering out of the box.
Moreover, specifying argument ìnstance=request.user is incorrect because if you have a DetailForm the instance should be a model instance of the Detail model, not User model. Keep in mind, instance kwarg is only necessary if you want to use this form for updating.
I suggest an implementation similar to the following:
forms.py
class DetailForm(ModelForm):
class Meta:
model = Detail
fields = ['user', 'file', 'file_desc']
widgets = {
# I'm guessing you don't want to see the User field
'user': HiddenInput()
}
Relevant part of views.py
def get(self, request):
form = DetailForm()
context = {'form': form}
return render(request, self.template_name, context)
def post(self, request):
form = DetailForm(request.POST, request.FILES, initial={'user': request.user.id})
if form.is_valid():
form.save()
return redirect('user')
context = {'form': form}
return render(request, self.template_name, context)
To make this a complete answer, I will include a comment from #raphael which fixed the first issue of your code:
Make sure your form tag is as follows:
<form method="post" enctype="multipart/form-data">
Some resources to follow:
Form and field validations
Creating forms from models
In my app, I have created a context_proccessors.py to show the form to base.html file.
I am able to show the form in the base.html file. But the problem I am facing is I have no idea how to save that form data from base.html since there is no view for the base.html. Below is my code:
models.py
class Posts(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_posts')
post_pic = models.ImageField(upload_to='post_pic', verbose_name="Image")
post_caption = models.TextField(max_length=264, verbose_name="Caption")
created_date = models.DateTimeField(auto_now_add=True)
edited_date = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.username}"
forms.py
from django import forms
from post_app.models import Posts
class PostForm(forms.ModelForm):
class Meta:
model = Posts
exclude = ('user',)
context_proccessors.py
from post_app.forms import PostForm
def post_form(request):
form = PostForm
return {
'post_form': form,
}
base.html
<form method="POST" enctype="multipart/form-data">
{{ post_form|crispy }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">Post</button>
</form>
I want the form to be displayed on every page so that the user can submit data from anywhere
def PostView(request):
form = PostForm()
if request.method == 'GET':
return render(request, 'base.html', {form:form})
elif request.method == 'POST':
form.save(request.data)
In the views.py of your app you can define this view, and the you have to provide it an url in the urls.py of the root directory. So evere time there is a request on that url, if the method is GET, the form will be rendered on base.html file, if the method is POST, the post will be saved.
By following the answer by N T I have implemented this. So, I had to make a URL pattern for the view and use that URL pattern in the action in the form of base.html.
view.py
#login_required
def postsaveview(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user_obj = form.save(commit=False)
user_obj.user = request.user
user_obj.slug = str(request.user) + str(uuid.uuid4())
user_obj.save()
return HttpResponseRedirect(reverse('profile_app:profile'))
urls.py
urlpatterns = [
path('post-save/', views.postsaveview, name='post-save'),
]
base.html
<form action="{% url "post-save" %}" method="POST" enctype="multipart/form-data">
{{ post_form|crispy }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">Post</button>
</form>
I created a Form using one of my models i.e (Post), for my blog website. The form is meant for writers to post articles. In that form there is an Image attribute where the writer can upload an image. However, when i try to upload an image and post it, i get a feedback saying "field required", i think the form is not recognizing the image am trying to upload onto the the database. please help:
this is the form view from views.py:
def formview(request):
form = PostForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return render(request, 'form.html', {'form':form})
this is from forms.py:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
image = forms.FileField
class Meta:
model = Post
fields = ['category', 'title', 'body', 'image', 'author']
this from my models.py:
class Post(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
pub_date = models.DateTimeField(auto_now_add=True)
body = models.TextField()
image = models.FileField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.IntegerField(default=1)
def __str__(self):
return self.title
this is my forms.html template:
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Post</button>
this is my urls.py:
from django.conf.urls import url
from . import views
app_name = 'posts'
urlpatterns = [
url(r'^$', views.homeview, name='homeview'),
url(r'^(?P<pk>[0-9]+)$', views.postview, name='postview'),
url(r'^category/(?P<pk>[a-zA-Z0-9]+)/$', views.categoryview,
name='categoryview'),
url(r'^author/(?P<pk>[a-zA-Z0-9]+)/$', views.authorview, name='authorview'),
url(r'^add_post/$', views.formview, name='formview'),
]
these are the pics might help explain what am trying to say:
Filling the form and selecting the picture
Error message after trying to post
Thank you
def formview(request):
if request.method == 'POST':
form = PostForm(request.POST,request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
else:
form = PostForm()
return render(request, 'form.html', {'form':form})
this form = PostForm(request.POST,request.FILES),you need add FILES to PostForm
I can't seem to get a model form to load in my template.
models.py
from django.db import models
class Event(models.Model):
name = models.CharField(max_length=60)
start_datetime = models.DateTimeField()
end_datetime = models.DateTimeField()
description = models.TextField()
forms.py
from django import forms
from .models import Event
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = ['name']
views.py
from django.shortcuts import render
from .forms import EventForm
def index(request):
if request.method == 'POST':
form = EventForm(request.POST)
if form.is_valid():
form.save()
else:
form = EventForm()
return render(request, 'index.html')
index.html
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
</form>
<button type="submit">Save</button>
I can get the form to print to the console on load when adding print(form) in views.py on the GET request, but it doesn't load in the template.
Good examples on different ways to use forms : https://docs.djangoproject.com/en/1.11/topics/forms/#the-view
For index.html to render it is expecting form variable. So Render method call should be like this:
render(request, 'index.html', {'form': form})