I am working on CS50W project 2 - commerce and I've been working on the Create Listing point. I have a route /create where I have to get data and save it in models. I'll leave the code snippets below . I tried to use the ModelForm class , then validate the data and save() but it doesn't save data . I also ran queries in shell on the models to see if data is there but nothing. In the code snippets I'll leave only the useful parts to look cleaner. I checked and the imports ( importing models to views , other modules , etc) are fine. I think I made a mistake somewhere else , but I just can't see it. How to solve this?
the ModelForm
class AuctionsForm(forms.ModelForm):
class Meta:
model = Auctions
fields = ['title' , 'description' , 'category' , 'image']
views.py
#login_required
def create(request):
if request.method == "POST":
form = AuctionsForm(request.POST)
if form.is_valid():
form.save()
else:
form = AuctionsForm()
return render(request, "auctions/create.html" , {"form" : AuctionsForm()} )
models.py
class Auctions(models.Model):
CATEGORY = [
("No Category" , "No Category"),
("Fashion" , "Fashion"),
("Home" , "Home"),
("Toys" , "Toys"),
("Technology" , "Technology"),
]
title = models.CharField(max_length= 50)
description = models.TextField(max_length=150)
category = models.CharField(max_length = 40 , choices= CATEGORY , default="None")
image = models.URLField(default="" , blank=True)
is_active = models.BooleanField(default=True)
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey('auctions.User' , on_delete=models.CASCADE , related_name='user_auctions')
watchlist = models.ForeignKey('auctions.User' , on_delete=models.CASCADE , related_name='watchlist_auctions')
def __str__(self):
return self.title
create.html
{% extends "auctions/layout.html" %}
{% block body %}
<h1>Create Listing Here</h1>
<form method="POST" action="{% url 'index' %}">
{% csrf_token %}
{{form}}
<input type="submit" value="Create Listing">
</form>
{% endblock %}
index.html
{% extends "auctions/layout.html" %}
{% block body %}
<h2>Active Listings</h2>
{% for i in show%}
<p>{{i.title}}</p>
<p>{{i.description}}</p>
{% endfor%}
{% endblock %}
forms.py
class AuctionsForm(forms.ModelForm):
class Meta:
model = models.Auction
fields = ['title', 'description', 'category', 'image']
views.py
def create_auction(request):
if request.method == 'POST':
form = AuctionsForm(request.POST)
if form.is_valid():
auction = form.save(commit=False)
# do something if you but in the end
auction.save()
# do want you want here, or return in to creation page dunno
return render(request, "success?idk/", {"auction": auction})
else:
# maybe you want to render the errors?
# https://stackoverflow.com/questions/14647723/django-forms-if-not-valid-show-form-with-error-message
return render(request, "auctions/create.html", {"form": form})
form = AuctionForm()
return render(request, "auction/create.html", {"form":form})
# maybe you had listing view here
use Auction instead of Auctions, Django pluralize(s) it
Related
I am learning an authentication in Django. Today I am stuck with this problem and I am not able to figure out how can I solve it. I want to make a form for password change. but the change form link is not active and I don't know how to fix it.
here is my views.py
#login_required
def user_change(request):
current_user = request.user
form = UserProfileChange(instance = current_user)
if request.method == 'POST':
form = UserProfileChange(request.POST,instance = current_user)
if form.is_valid():
form.save()
form = UserProfileChange(instance = current_user)
form = UserProfileChange(instance = current_user)
return render(request, 'App_Login/change_profile.html', context={'form':form})
#login_required
def pass_change(request):
current_user = request.user
form = PasswordChangeForm(current_user)
if request.method == 'POST':
form = PasswordChangeForm(current_user, data = request.POST)
if form.is_valid():
form.save()
return render(request, 'App_login/pass_change.html', context = {'form':form})
here is urls.py file
from django.urls import path
from . import views
app_name = 'App_Login'
urlpatterns = [
path('signup/', views.signup, name = "signup"),
path('signin/', views.login_page, name = 'signin'),
path('logout/' , views.logout_user, name = "logout" ),
path('profile/' , views.profile, name = "profile" ),
path('change-profile/' , views.user_change, name = "user_change" ),
path('password/' , views.pass_change, name = "pass_change" ),
]
here is change_profile.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %} change User profile {% endblock %}
{% block body_block %}
<h2>Change Profile</h2>
<form method="post">
{% csrf_token %}
{{form|crispy}}
<input type="submit" value="Change" class = "btn btn-primary btn-sm">
</form>
{% endblock %}
here is forms.py file
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth.models import User
class SignupForm(UserCreationForm):
email = forms.EmailField(label = "Email Address", required=True)
class Meta:
model = User
fields = ('username','email', 'password1', 'password2')
class UserProfileChange(UserChangeForm):
class Meta:
model = User
fields = ('username','email','first_name', 'last_name', 'password')
Django has an inbuilt functionality to do that in your urls file modify it to according your needs
from django.contrib.auth import views as auth_views
urlpatterns = [
#
# Rest Of Your Code
#
path('password/',auth_views.PasswordChangeView.as_view(
template_name='template to render here',
success_url = 'page to redirect after password is changed'),name='password')
]
I am looking to modify a form from the user and return that different form in django, however I have tried many different ways, all in views.py, including:
Directly modifying it by doing str(form) += "modification"
returning a new form by newform = str(form) + "modification"
creating a different Post in models, but then I realized that wouldn't work because I only want one post
All the above have generated errors such as SyntaxError: can't assign to function call, TypeError: join() argument must be str or bytes, not 'HttpResponseRedirect', AttributeError: 'str' object has no attribute 'save', and another authority error that said I can't modify a form or something like that.
Here is a snippet from views.py:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['content']
title = ['title'] #
template_name = 'blog/post_new.html'
success_url = '/'
def form_valid(self, form):
#debugging
tempvar = (str(form).split('required id="id_content">'))[1].split('</textarea></td>')[0] #url
r = requests.get(tempvar)
tree = fromstring(r.content)
title = tree.findtext('.//title')
print(title)
form.instance.author = self.request.user
if "http://" in str(form).lower() or "https://" in str(form).lower():
if tempvar.endswith(' '):
return super().form_valid(form)
elif " http" in tempvar:
return super().form_valid(form)
elif ' ' not in tempvar:
return super().form_valid(form)
else:
return None
models.py:
class Post(models.Model):
content = models.TextField(max_length=1000)
title = models.TextField(max_length=500, default='SOME STRING') #
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes= models.IntegerField(default=0)
dislikes= models.IntegerField(default=0)
def __str__(self):
return (self.content[:5], self.title[:5]) #
#property
def number_of_comments(self):
return Comment.objects.filter(post_connected=self).count()
And in home.html, where the post (along with the title and content) is supposed to be shown:
<a
style="color: rgba(255, 255, 255, 0.5) !important;"
href="{% url 'post-detail' post.id %}">
<p class="mb-4">
{{ post.content }}
{{ post.title }} #
</p>
</a>
The original template I'm modifying can be found here.
Thank you so much for your help, I will be very glad to take any advice!!
Ps: I'm using Python 3.7.4
Create a forms.py file inside the app you are talking about, it should look like this:
from django import forms
from . import models
class YourFormName(forms.ModelForm):
class Meta:
model = models.your_model_name
fields = ['field1', 'field2' ,...] # Here you write the fields of your model, this fields will appear on the form where user post data
Then you call that form into your views.py so Django can render it into your template, like this:
def your_view(request, *args, **kwargs):
if request.method == 'POST':
form = forms.YourFormName(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user= request.user
instance.save()
return redirect('template.html') # To redirect if the form is valid
else:
form = forms.YourFormName()
return render(request, "template.html", {'form': form}) # The template if the form is not valid
And the last thing to do is create the template.html:
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'the_url_that_renders_this_template' %}" method='POST' enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<button type="submit">Submit</button>
</form>
{% endblock content %}
If you want to take the data from DB submitted in that form, you do so with a new function in views.py:
def show_items(request, *args, **kwargs):
data = YourModelName.objects.all()
context = {
"data": data
}
return render(request, "show_items.html", context)
Then in show_items.html:
{% extends 'base.html' %}
{% block content %}
{% for item in data %}
{{item.field1}}
{{item.field2}}
...
{{The items you want to show in that template}}
{% enfor %}
{% endblock content %}
That's what you wanted to do? If not, add a further explanation on what actually you want to do
This is function for updating user's info.
views.py
def UpdateProfile(request):
context = {}
user = request.User
if not user.is_authenticated:
return redirect('login')
if request.POST:
form = PersonalInfo(request.POST, instance=user)
if form.is_valid():
obj = form.Save(commit=False)
user = user.id
obj.user = user
obj.save()
return redirect('profile')
else:
#messages.error(request, ('Please correct the error below.'))
context['personal_form'] = form
else:
form = PersonalInfo(instance=user)
context['personal_form'] = form
return render(request, 'admission/signup.html', context)
This is the model I have created for storing user info.
models.py:
class ApplicantInfo(models.Model):
infield = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
profile_pic = models.ImageField(upload_to='media/', blank= True, null= True)
father_name = models.CharField(max_length=30)
user = models.OneToOneField(User, on_delete=models.CASCADE)
This is the form class I have created.
forms.py:
from .models import Applicant, ApplicantInfo
from django import forms
class PersonalInfo(forms.ModelForm):
class Meta:
model = ProfileInfo
fields = [
'first_name',
'last_name',
'profile_pic',
#'date_birth',
'father_name',
'street_adr',
'city',
'zip_code',
]
This is the frontend template which I have created, this is working fine.
Template
{%block content%}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form method="POST">
{% csrf_token %}
{% for field in personal_form %}
<p>
{{field.label_tag}}
{{field}}
{% if field.help_text %}
<small style="color:gray">{{field.help_text}}</small>
{% endif %}
</p>
{% endfor %}
{% for field in personal_form %}
<p>
{% for error in field.errors %}
<p style="color:red">{{error}}</p>
{% endfor %}
</p>
{% endfor %}
<button type="submit">Submit</button>
</form>
</body>
</html>
{% endblock content %}
The app has been able to load the form without any trouble but it just not throwing data to the backend.
Updated function for UpdateProfile
views.py
def UpdateProfile(request, id=None):
context = {}
user_obj = request.user
if not user_obj.is_authenticated:
return redirect('login')
if request.method == "POST":
form = PersonalInfo(request.POST)
if form.is_valid():
obj = form.save()
user_id = Applicant.objects.filter(app_id = user_obj.app_id).first()
obj.user = user_id
obj.save()
return redirect('profile')
else:
#messages.error(request, ('Please correct the error below.'))
context['personal_form'] = form
elif request.method == 'GET':
form = PersonalInfo(instance=user_obj)
context['personal_form'] = form
return render(request, 'admission/signup.html', context)
Updated code for forms method, only add #def cleaned_data function
forms.py
class PersonalInfo( forms.ModelForm):
class Meta:
model = ProfileInfo
fields = [
'first_name',
'last_name',
'profile_pic',
#'date_birth',
'father_name',
'street_adr',
'city',
'zip_code',
]
def clean(self):
if self.is_valid():
first_name = self.cleaned_data['first_name']
last_name = self.cleaned_data['last_name']
father_name = self.cleaned_data['father_name']
street_adr = self.cleaned_data['street_adr']
city = self.cleaned_data['city']
zip_code = self.cleaned_data['zip_code']
And model does not need to be change, you have choice either to add and autofield(PrimaryKey) or use the default one
The problem was in view.py where i just removed instance attribute from the form object, then I called user_id from user model which is in this case #Applicant model, then assigned to the obj object and saved obj.
Thanks to all of you who has spent precious time in this problem.
Cheers!!!
I tried to make a blog that allows user posts, store the posts in the db along with the posted date time and the person who posted it.
My problem is that I somehow cannot load the {{form}} to my UI, which makes my form invalid and I just don't know why it doesn't show up the input text box.
I'm not sure if I need a get_post function, but I'll just put it in views.py. (I havnt write the html of that part yet. Just need to see the form first.)
I'm pretty new to Django, can somebody pls help me with this!!! Thanks!
Below are my files.
blog.html file:
{% block posts %}
<div>
<span>New Post: </span>
<form method="post" action="{% url 'posts' %}" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{form}}
<!--not showing in UI-->
</table>
<input id="id_post_button" type="submit" value="Submit" /><br>
</form>
<div>
{% endblock %}
urls.py
urlpatterns = [
path('posts', views.post_action, name='posts'),
path('post/<int:id>', views.get_post, name='post'),
]
Models.py
class PostModel(models.Model):
user_id = models.IntegerField()
post_input_text = models.CharField(max_length=100)
post_profile = models.CharField(max_length=30)
post_date_time = models.DateTimeField(default=timezone.now)
def __str__(self):
return 'id=' + str(self.user_id) + ", post_date_time=" + self.post_date_time + ", post_input_text=" + self.post_input_text + ", post_profile=" + self.post_profile
Views.py:
#login_required
def post_action(request):
print("----post action---")
context = {}
if request.method == "GET":
context['form'] = CreatePost()
context['posts']= PostModel.objects.get(user_id = request.user.id)
return render(request, "socialnetwork/blog.html", context)
form = CreatePost(request.POST, request.FILES)
if not form.is_valid():
print("not valid ~~~~~~~~")
context['form'] = form
context['posts'] = PostModel.objects.get(user_id = request.user.id)
return render(request, "socialnetwork/blog.html", context)
post_input_text = form.cleaned_data.get("post_input_text")
post_date_time = form.cleaned_data.get("post_date_time")
post_profile = form.cleaned_data.get("post_profile")
obj = PostModel.objects.get(
user_id = request.user.id,
)
obj.post_input_text = form.cleaned_data.get("post_input_text")
obj.post_date_time = form.cleaned_data.get("post_date_time")
obj.post_profile = form.cleaned_data.get("post_profile")
obj.save()
form = CreatePost() #refresh the form to original state
context['form'] = form
context['posts'] = obj
return render(request, "socialnetwork/blog.html", context)
def get_post(request, id):
item = get_object_or_404(PostModel, id=id)
print('Picture #{} fetched from db: {} (type={})'.format(id, item.post_input_text, item.post_profile, item.post_date_time))
if not item.post_input_text:
raise Http404
return HttpResponse(item.post_input_text)
forms.py
class CreatePost(forms.Form):
post_input_text = forms.CharField(max_length=100)
post_profile = forms.CharField(max_length=30)
post_date_time = forms.DateTimeField()
Update the template with {{ form.as_table }}, instead of {{form}}
I'm not sure how to filter dropdown based on user id.
Not I want for user id 2.
I want exactly like this for user id 2.
Model
#python_2_unicode_compatible # only if you need to support Python 2
class PredefinedMessage(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length=50)
list_description = models.CharField(max_length=50)
def __str__(self):
return self.list_name
class PredefinedMessageDetail(models.Model):
predefined_message_detail = models.ForeignKey(PredefinedMessage)
message = models.CharField(max_length=5000)
View
class PredefinedMessageDetailForm(ModelForm):
class Meta:
model = PredefinedMessageDetail
fields = ['predefined_message_detail', 'message']
exclude = ('user',)
def predefined_message_detail_update(request, pk, template_name='predefined-message/predefined_message_detail_form.html'):
if not request.user.is_authenticated():
return redirect('home')
predefined_message_detail = get_object_or_404(PredefinedMessageDetail, pk=pk)
form = PredefinedMessageDetailForm(request.POST or None, instance=predefined_message_detail)
if form.is_valid():
form.save()
return redirect('predefined_message_list')
return render(request, template_name, {'form':form})
html file
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
You can do it in view itself using
form = PredefinedMessageDetailForm(request.POST or None, instance=predefined_message_detail)
form.fields["predefined_message_detail"].queryset= PredefinedMessage.objects.filter(user=request.user)
But filtering happens based on request.user so it should be logged in.Consider that also. Hope this helps