I am trying to make a matchmaking project on python django such as snapchat's hoop, but first I am trying to do the easiest part(what I know) which is just making the user create a profile to find a mate and then show that created profile to the other users, currently I am not using any matchmaking based on questions, I need to first show the created user to other users. The error comes when I pass the variables to the html via for loops, and now the mates.html file doesnt show anything. Please if you need more explanation let me know in the comments.
models.py (Profile model show the profile of the user which is created by default wen the user is created and Mates model shows the profile that the user has to create which provide an image and a description)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(max_length=400, default=1, null=True)
class Mates(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user")
users_requests = models.ManyToManyField(User, related_name="users_requests")
req_bio = models.CharField(max_length=400)
req_image = models.ImageField(upload_to='requestmates_pics', null=True, blank=True, default=False)
views.py
def matesmain(request):
contents = Mates.objects.all()
args123 = {
'contents': contents,
}
return render(request, 'mates.html', args123)
def mates(request):
if request.method == 'POST':
form_mates = MatesForm(request.POST, request.FILES)
if form_mates.is_valid():
instance = form_mates.save(commit=False)
instance.user = request.user
instance.save()
return redirect('mates-main')
print('succesfully uploded')
else:
form_mates = MatesForm()
print('didnt upload')
return render(request, 'mates.html', {'form_mates': form_mates})
forms.py
class MatesForm(forms.ModelForm):
class Meta:
model = Mates
fields = ('req_bio', 'req_image',)
urls.py
urlpatterns = [
path('mates', views.mates, name='mates'),
path('mates-main', views.matesmain, name='mates-main'),
]
mates.html
<div class="mates">
<div class="mates-container">
{% for content in contents %}
<div class="mates-item">{{ content.user }}
</div>
{% endfor %}
<div class="mates-item">content(picture)
<form action="{% url 'mates' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form_mates.as_p }}
</form>
</div>
</div>
</div>
If you are displaying the form with the second function (mates), then you aren't sending any context for your fields to iterate over. Try like so:
def mates(request):
if request.method == 'POST':
form_mates = MatesForm(request.POST, request.FILES)
if form_mates.is_valid():
instance = form_mates.save(commit=False)
instance.user = request.user
instance.save()
return redirect('mates-main')
print('succesfully uploded')
else:
form_mates = MatesForm()
print('didnt upload')
context = {
'form_mates': form_mates,
'contents': Mates.objects.all()
}
return render(request, 'mates.html', context)
and in your other view (matesmain):
contents = Mates.objects.all()
args123 = {
'contents': contents,
'form_mates': MatesForm()
}
return render(request, 'mates.html', args123)
I'm not sure I understand, however, why you have two separate views for this.
Related
I have a Django view that shows two create forms.
Whenever the page loads all of the input fields display - 'This field is required".
enter image description here
Template code
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ listing_create_form.as_p }}
{{ listing_media_form.as_p }}
<button type="submit">Submit Form</button>
</form>
{% endblock %}
views.py
#login_required
def createListing(request):
listing_create_form = ListingCreateForm(request.POST or None, request.FILES)
listing_media_form = ListingMediaForm(request.POST or None, request.FILES)
if request.method == 'POST':
if listing_create_form.is_valid() and listing_media_form.is_valid():
listing_create_form.instance.created_by = request.user
form = listing_create_form.save()
form.save()
new_listing_id = form.pk
# loop over images to upload multiple
for image_uploaded in request.FILES.getlist('image'):
image_instance = ListingMedia.objects.create(listing=form, image=image_uploaded)
image_instance.save()
return redirect('boat_listings')
context = {'listing_create_form': listing_create_form, 'listing_media_form': listing_media_form}
return render(request, 'listings/listing_create_form.html', context)
forms.py
class ListingCreateForm(forms.ModelForm):
class Meta:
model = Listings
widgets = {
"featured_image": forms.FileInput(
attrs={
"enctype": "multipart/form-data"
}
),
}
fields = "__all__"
exclude = ("created_by", "created_on", "last_modified",)
class ListingMediaForm(forms.ModelForm):
class Meta:
# image = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
widgets = {
"image": forms.ClearableFileInput(
attrs={
"multiple": True
}
),
}
model = ListingMedia
fields = ['image']
Django template should render without field required message before user has inputted invalid inputs.
it looks like when you initialise form instances, it gets data and tries to validate so that's what you are receiving error messages.
change your view:
#login_required
def createListing(request):
if request.method == 'POST':
listing_create_form = ListingCreateForm(request.POST, request.FILES)
listing_media_form = ListingMediaForm(request.POST, request.FILES)
if listing_create_form.is_valid() and listing_media_form.is_valid():
listing_create_form.instance.created_by = request.user
form = listing_create_form.save()
form.save()
new_listing_id = form.pk
# loop over images to upload multiple
for image_uploaded in request.FILES.getlist('image'):
image_instance = ListingMedia.objects.create(listing=form, image=image_uploaded)
image_instance.save()
return redirect('boat_listings')
else:
listing_create_form = ListingCreateForm()
listing_media_form = ListingMediaForm()
context = {'listing_create_form': listing_create_form, 'listing_media_form': listing_media_form}
return render(request, 'listings/listing_create_form.html', context)
I have created a forum website in Django where users can post Questions/Answers and edit them.
After editing the reply I want to redirect the user to the currently edited post page. like if user
edit reply which has been posted on the question with id 4 (which url is (http://127.0.0.1:8000/discussion/4)) then after edited it should redirect to the same URL. After editing, and deleting the reply I am redirecting the user to the forum homepage but I want to redirect to the /discussion/{post_id} URL(which is URL of the particular post on which reply being edited and deleted)
urls.py
app_name = "dashboard"
urlpatterns = [
path('', views.index, name="index"),
path('user_home', views.user_home, name="user_home"),
path('admin_home', views.admin_home, name="admin_home"),
path("forum", views.forum, name="forum"),
path("discussion/<int:myid>", views.discussion, name="discussion"),
path("showallusers", views.show_all_users, name="showallusers"),
path('delete_user/<int:pk>', views.delete_user, name="delete_user"),
path('delete_post/<int:pk>', views.delete_post, name="delete_post"),
path('delete_reply/<int:pk>', views.delete_reply, name="delete_reply"),
path('upload_notes', views.upload_notes, name='upload_notes'),
path('view_mynotes', views.view_mynotes, name='view_mynotes'),
path('delete_mynotes/<int:pk>/', views.delete_mynotes, `name='delete_mynotes'), `
path('pending_notes', views.pending_notes, name='pending_notes'),
path('assign_status/<int:pk>', views.assign_status, name='assign_status'),
path('accepted_notes', views.accepted_notes, name='accepted_notes'),
path('rejected_notes', views.rejected_notes, name='rejected_notes'),
path('all_notes', views.all_notes, name='all_notes'),
path('delete_notes/<int:pk>', views.delete_notes, name='delete_notes'),
path('delete-records/', views.delete_notes, name='delete_notes'),
path('view_allnotes', views.view_allnotes, name='view_allnotes'),
path('notessharing', views.notessharing, name='notessharing'),
path('edit_post/<int:pk>/', views.edit_post, name='edit_post'),
path('edit_reply/<int:pk>/', views.edit_reply, name='edit_reply'),
]
delete_reply code
def delete_reply(request, pk=None):
reply = Replie.objects.filter(id=pk)
reply.delete()
return redirect('/forum')
After deleting a reply from a post I want to redirect to the same post.
models.py
class Post(models.Model):
user1 = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
post_id = models.AutoField
post_content = models.TextField(max_length=5000,verbose_name="")
timestamp= models.DateTimeField(default=now)
image = models.ImageField(upload_to="images",default="")
def __str__(self):
return f'{self.user1} Post'
class Replie(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
reply_id = models.AutoField
reply_content = models.TextField(max_length=5000,verbose_name="")
post = models.ForeignKey(Post, on_delete=models.CASCADE, default='')
timestamp= models.DateTimeField(default=now)
image = models.ImageField(upload_to="images",default="")
def __str__(self):
return f'{self.user1} Post'
views.py
def forum(request):
user = request.user
profile = Profile.objects.all()
if request.method=="POST":
form=PostContent(request.POST)
if form.is_valid():
user = request.user
image = request.user.profile.image
content = request.POST.get('post_content','')
post = Post(user1=user, post_content=content, image=image)
post.save()
messages.success(request, f'Your Question has been posted successfully!!')
return redirect('/forum')
else:
form=PostContent()
posts = Post.objects.filter().order_by('-timestamp')
form= PostContent()
context={
'posts':posts,
'form':form
}
return render(request, "forum.html",context)
def discussion(request, myid):
post = Post.objects.filter(id=myid).first()
replies = Replie.objects.filter(post=post)
if request.method=="POST":
form=ReplyContent(request.POST)
if form.is_valid():
user = request.user
image = request.user.profile.image
desc = request.POST.get('reply_content','')
post_id =request.POST.get('post_id','')
reply = Replie(user = user, reply_content = desc, post=post, image=image)
reply.save()
messages.success(request, f'Your Reply has been posted successfully!!')
return redirect(f'/discussion/{post_id}')
else:
form=ReplyContent()
form= ReplyContent()
return render(request, "discussion.html", {'post':post, 'replies':replies,'form':form})
def edit_reply(request, pk):
reply = Replie.objects.get(id=pk)
if request.method == 'POST':
form = UpdateReplyForm(request.POST, instance=reply)
if form.is_valid():
form.save()
messages.success(request,"Reply updated successfully!")
return redirect('/forum')
else:
form = UpdateReplyForm(instance=reply)
context = {
'form': form
}
return render(request, 'edit_reply.html', context)
edit_post view
def edit_post(request, pk):
post = Post.objects.get(id=pk)
if request.method == 'POST':
form = UpdatePostForm(request.POST, instance=post)
if form.is_valid():
form.save()
messages.success(request, "Post updated successfully!")
return redirect('/forum')
else:
form = UpdatePostForm(instance=post)
context = {
'form': form
}
return render(request, 'edit_post.html', context)
Currently, after editing the reply, I am redirecting the user to the post home page but I want to redirect to /discussion/{post_id}.
Template code:
edit_reply.html
{% load static %}
{% block body %}
{% load crispy_forms_tags %}
<div class="container ">
<form method="POST">
<div class="form-group">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4 mt-4 f2" >Update Reply</legend>
</fieldset>
<label style="font-size:1rem; font-weight:bold;">Reply Content</label>
{{form|crispy}}
<input type="hidden" name="post_id" value="{{post_id}}">
<div class="form-group">
<button href="" class="btn btn-primary" type="Update">
Update
</button>
</div>
</div>
</form>
</div>
{% endblock body %}
You can redirect it to discussion view with its pk by passing args=[reply.post.id] in edit_reply view.
Try this:
views.py
from django.shortcuts import render, redirect
from django.urls import reverse
def edit_reply(request, pk):
reply = Replie.objects.get(id=pk)
if request.method == 'POST':
form = UpdateReplyForm(request.POST, instance=reply)
if form.is_valid():
form.save()
messages.success(request, "Reply updated successfully!")
return redirect(reverse('dashboard:discussion', args=[reply.post.id]))
else:
form = UpdateReplyForm(instance=reply)
context = {
'form': form
}
return render(request, 'home/edit_reply.html', context)
Note: Forms in django required Form to be the suffix, so it will be better if it changed to PostContentForm and ReplyContentForm from PostContent and ReplyContent respectively.
It must be return f'{self.user} Post' not
return f'{self.user`} Post'
As it is not any field in Replie model.
Note: ForeignKey's names are generally written in its own name and that too in snake_case, it will be better if you change user1 to user in Post model.
For better understanding:
If table name is PizzaTopingCategory so while creating ForeignKey you should name it as pizza_toping_category=models.ForeignKey(PizzaTopingCategory, on_delete=models.CASCADE)
Edit:
You need to find out post_id, so you can send it through:
Try this in the delte_reply view:
def delete_reply(request, pk=None):
reply = Replie.objects.filter(id=pk)
reply_instance = get_object_or_404(Replie,id=pk)
post_pk=reply_instance.post.id
reply.delete()
return redirect(reverse('dashboard:discussion', args=[post_pk]))
For passing pk in discussion view, you should write return redirect(reverse('dashboard:discussion', args=[reply.post.id])) in edit_post view:
views.py
def edit_post(request, pk):
post = Post.objects.get(id=pk)
if request.method == 'POST':
form = UpdatePostForm(request.POST, instance=post)
if form.is_valid():
form.save()
messages.success(request, "Post updated successfully!")
return redirect(reverse('dashboard:discussion', args=[post.id]))
else:
form = UpdatePostForm(instance=post)
context = {
'form': form
}
return render(request, 'home/edit_post.html', context)
i am new in Django. i am having issue in updating ImageField.i have following code
in models.py
class ImageModel(models.Model):
image_name = models.CharField(max_length=50)
image_color = models.CharField(max_length=50)
image_document = models.ImageField(upload_to='product/')
-This is My forms.py
class ImageForm(forms.ModelForm):
class Meta:
model = ImageModel
fields = ['image_name', 'image_color' , 'image_document']
in Html file (editproduct.html)
<form method="POST" action="/myapp/updateimage/{{ singleimagedata.id }}">
{% csrf_token %}
<input class="form-control" type="text" name="image_name" value="{{ singleimagedata.image_name}}">
<input class="form-control" type="file" name="image_document">
<button type="submit" class="btn btn-primary">UPDATE PRODUCT</button>
</form>
-myapp is my application name. {{singleimagedata}} is a Variable Containing all fetched Data
-urls.py
urlpatterns = [
path('productlist', views.productlist, name='productlist'),
path('addproduct', views.addproduct, name='addproduct'),
path('editimage/<int:id>', views.editimage, name='editimage'),
path('updateimage/<int:id>', views.updateimage, name='updateimage'),
]
and Here is My views.py
def productlist(request):
if request.method == 'GET':
imagedata = ImageModel.objects.all()
return render(request,"product/productlist.html",{'imagedata':imagedata})
def addproduct(request):
if request.method == 'POST':
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS, 'Image Uploaded')
return redirect('/myapp/productlist')
else:
imageform = ImageForm()
return render(request, "product/addproduct.html", {'imageform': imageform})
def editimage(request, id):
singleimagedata = ImageModel.objects.get(id=id)
return render(request, 'product/editproduct.html', {'singleimagedata': singleimagedata})
def updateimage(request, id): #this function is called when update data
data = ImageModel.objects.get(id=id)
form = ImageForm(request.POST,request.FILES,instance = data)
if form.is_valid():
form.save()
return redirect("/myapp/productlist")
else:
return render(request, 'demo/editproduct.html', {'singleimagedata': data})
My image Upload is working fine.i can not Update image while updating data.rest of the data are updated.i don't know how to update image and how to remove old image and put new image into directory.
I think you missed the enctype="multipart/form-data", try to change:
<form method="POST" action="/myapp/updateimage/{{ singleimagedata.id }}">
into;
<form method="POST" enctype="multipart/form-data" action="{% url 'updateimage' id=singleimagedata.id %}">
Don't miss also to add the image_color field to your html input.
Because, in your case the image_color field model is designed as required field.
To remove & update the old image file from directory;
import os
from django.conf import settings
# your imported module...
def updateimage(request, id): #this function is called when update data
old_image = ImageModel.objects.get(id=id)
form = ImageForm(request.POST, request.FILES, instance=old_image)
if form.is_valid():
# deleting old uploaded image.
image_path = old_image.image_document.path
if os.path.exists(image_path):
os.remove(image_path)
# the `form.save` will also update your newest image & path.
form.save()
return redirect("/myapp/productlist")
else:
context = {'singleimagedata': old_image, 'form': form}
return render(request, 'demo/editproduct.html', context)
I had a similar issue while updating the profile_pic of user. I solved this with the following code I think this might help:
Models.py
class Profile(models.Model):
# setting o2o field of user with User model
user_name = models.OneToOneField(User, on_delete=models.CASCADE, blank=True, null=True)
first_name = models.CharField(max_length=70, null=True, blank=True)
last_name = models.CharField(max_length=70, null=True, blank=True)
profile_pic = models.ImageField(upload_to="images", blank=True, null=True,)
def __str__(self):
return str(self.user_name)
forms.py
class ProfileEditForm(ModelForm):
class Meta:
model = Profile
fields = '__all__'
# excluding user_name as it is a one_to_one relationship with User model
exclude = ['user_name']
views.py
#login_required(login_url='login')
def edit_profile(request, id):
username = get_object_or_404(Profile, id=id)
extended_pro_edit_form = ProfileEditForm(instance=username)
if request.method == "POST":
extended_pro_edit_form = ProfileEditForm(request.POST, request.FILES, instance=username)
if extended_pro_edit_form.is_valid():
extended_pro_edit_form.save()
next_ = request.POST.get('next', '/')
return HttpResponseRedirect(next_)
context = {'extended_pro_edit_form': extended_pro_edit_form}
return render(request, 'edit_profile.html', context)
edit-profile.html
<form action="" method="post"
enctype="multipart/form-data">
{% csrf_token %}
{{ extended_pro_edit_form.as_p }}
{{ extended_pro_edit_form.errors }}
<!--To redirect user to prvious page after post req-->
<input type="hidden" name="next" value="{{ request.GET.next }}">
<button type="submit">UPDATE</button>
</form>
Answer from #binpy should solve your problem. In addition to your second answer, you could do:
def updateimage(request, id): #this function is called when update data
data = ImageModel.objects.get(id=id)
form = ImageForm(request.POST,request.FILES,instance = data)
if form.is_valid():
data.image_document.delete() # This will delete your old image
form.save()
return redirect("/myapp/productlist")
else:
return render(request, 'demo/editproduct.html', {'singleimagedata': data})
Check delete() method on django docs.
some times something like cached old image is not replaced in the front-end so you might just need to forces refresh by pressing CTRL + F5 or clear your browsing history.
the answer given by #binpy is a needed update so that the files are passed to the back-end.
I have a form that involves uploading a profile picture. I have it working so that I can upload images in the /admin/ interface and display them correctly, but I cannot get my Modelform to save the image.
Here is what I have:
models.py
class Candidate(models.Model):
UserID = models.ForeignKey(User, on_delete=models.CASCADE)
ElectionID = models.ForeignKey(Election, on_delete=models.CASCADE)
Bio = models.CharField(max_length=500, blank=True)
ProfilePicture = models.ImageField(upload_to="profilepics/", null=True, blank=True)
forms.py
class AddCandidateForm(forms.ModelForm):
class Meta:
model = Candidate
fields = ['ElectionID', 'Bio', 'ProfilePicture']
cand_reg.html (Template)
{% block content %}
<h1>Register as a candidate</h1>
<form method="POST" class="post-form">
{% csrf_token %}
<h2>Select an election:</h2><br>
{{form.ElectionID}}<br>
<h2>Enter your bio:</h2><br>
{{form.Bio}}<br>
<h2>Upload a profile picture:</h2><br>
{{form.ProfilePicture}}<br>
<button type="submit">Register</button>
</form>
{% endblock %}
When I try the view function like so I get the error:
MultiValueDictKeyError at /register/
"'ProfilePicture'"
views.py
def add_candidate(request):
if request.method == 'POST':
form = AddCandidateForm(request.POST, request.FILES)
if form.is_valid():
candidate = form.save(commit=False)
candidate = request.FILES['ProfilePicture']
candidate.UserID = request.user
candidate.save()
return redirect('/home/')
else:
form = AddCandidateForm()
return render(request, 'cand_reg.html', {
"form": form
})
views.py
When I remove the offending line, the error goes away.
def add_candidate(request):
if request.method == 'POST':
form = AddCandidateForm(request.POST, request.FILES)
if form.is_valid():
candidate = form.save(commit=False)
# candidate = request.FILES['ProfilePicture']
candidate.UserID = request.user
candidate.save()
return redirect('/home/')
else:
form = AddCandidateForm()
return render(request, 'cand_reg.html', {
"form": form
})
However, this doesn't actually save the image, so when I try to render it in a separate template, I get an error then.
Can anyone help me understand why the image isn't uploading?
Thanks in advance :)
You must set the ProfilePicture attribute of the model and not the instance itself (candidate = request.FILES['ProfilePicture']).
Change to:
candidate = form.save(commit=False)
candidate.ProfilePicture = request.FILES['ProfilePicture']
candidate.UserID = request.user
candidate.save()
Change your HTML form to accept files as well. Change to: <form method="POST" enctype="multipart/form-data" class="post-form">. When a form includes file inputs (<input type="file" />), then it must be encoded differently than it used when it includes only text. More here. If you right-click and inspect the {{form.ProfilePicture}} you'll see that this is actually a file input.
Extra one:
Please, do not name your class attributes (ProfilePicture, UserID etc) in PascalCase. Use snake_case instead (profile_picture, user_id etc).
as title indicates I'm trying to send the name of a category user created by the current user on every page. My initial attempt was simply
{% if user.is_authenticated == category.author %}
{{category.name}}
{% endif %}
but this only displays the category in a certain page, while I want to display this in the navbar which I have it included for the every page. So I thought I should do category = models.foreignkey('category') in my user model but got told I should set a queryset in a template context processor. which I'm not sure it's the best way to do.
Can someone please direct me how I should do such matter?
here's my code
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
description = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL)
and in my views.py
#login_required
def add_category(request):
if not request.user.is_superuser and Category.objects.filter(author=request.user).exists():
return render(request,'main/category_already_exists.html')
if request.method == 'POST':
category = Category(author=request.user)
form = CategoryForm(request.POST, request.FILES, instance=category)
if form.is_valid():
form.save(commit=True)
return redirect('category', category_name_url=category.name)
else:
form = CategoryForm()
context = {
"form":form
}
return render(request, 'main/add_category.html',context)
and this is my simplified category view
def category(request, category_name_url):
category_name = decode_url(category_name_url)
category = Category.objects.get(name=category_name)
context = {
"category":category,
}
return render(request, "main/category.html", context)
and this is my model for my user
from userena.models import UserenaBaseProfile
class MyProfile(UserenaBaseProfile):
user = models.OneToOneField(User, unique=True, verbose_name=_('user'), related_name='my_profile')
Perhaps you should write your own context processor and include it in settings. Link to docs https://docs.djangoproject.com/en/dev/ref/templates/api/#context-processors
Definition from Django docs:
"Context processors are functions that receive the current HttpRequest as an argument and return a dict of data to be added to the rendering context.
Their main use is to add common data shared by all templates to the context without repeating code in every view."
You can add this information in request.session. You can do this like this:
Suppose your login view is this:
def login(request):
# Do login stuff
if user.is_active():
request.session['categories'] = [ c.name for c in Category.objects.filter(author=request.user)] # add to the session
return redirect('/somepage')
Display this data in every page like this:
{% for c in request.session.categories %}
{{ c }}
{% endfor %}
And update the category list every time the a new category is added like this:
#login_required
def add_category(request):
if not request.user.is_superuser and Category.objects.filter(author=request.user).exists():
return render(request,'main/category_already_exists.html')
if request.method == 'POST':
category = Category(author=request.user)
form = CategoryForm(request.POST, request.FILES, instance=category)
if form.is_valid():
form.save(commit=True)
request.session['categories'] = [ c.name for c in Category.objects.filter(author=request.user)]
return redirect('category', category_name_url=category.name)
else:
form = CategoryForm()
context = {
"form":form
}
return render(request, 'main/add_category.html',context)