I am very new to Python and Django and am stuck with this problem , which I think should be very simple to solve.
model.py
class UserDetails(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
billingAddress = AddressField(related_name='+',blank =True ) # Used django-address https://pypi.org/project/django-address/
shippingAddress = AddressField(related_name='+',blank =True)
forms.py
class AddressForm(forms.ModelForm):
class Meta:
model = UserDetails
exclude = ['user']
views.py
def address(request):
form = AddressForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
zipCode = request.POST.get("ZipCode","")
form = AddressForm(data=request.POST)
detailForm = form.save(commit = False)
detailForm.user = request.user
baddressDict = {'raw':request.POST.get("billingAddress","")+", " + zipCode, 'postal_code': zipCode,}
saddressDict = {'raw':request.POST.get("shippingAddress","")+", " + zipCode, 'postal_code': zipCode,}
detailForm.billingAddress = baddressDict
detailForm.shippingAddress = saddressDict
detailForm.save()
else:
form = AddressForm()
return render(request,'showcase/address.html',{'form': form})
address.html
<form action="." method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="text" name="ZipCode" value="Vip Code" >
<input type="submit" value="Submit" >
</form>
What I am trying to do it update the shipping & Billing address for current user.
The first time I am doing this it works but the second time it gives
UNIQUE constraint failed: showcase_userdetails.user_id
which obviously is cause it trying to add another row in the DB.
How do i make sure it updates and not insert?
Thanks,
Gourav
Quite simply, you have to pass an existing instance of your model:
def edit_address(request):
user = request.user
try:
address_instance = UserDetail.objects.get(user=user)
except UserDetail.DoesNotExist:
address_instance = None
if request.method == 'POST':
form = AddressForm(request.POST, instance=address_instance)
if form.is_valid():
details = form.save(commit=False)
# You should really let the form takes care of all this,
# and you should DEFINITLY NOT use unsanitized data from
# request.POST - the whole point of forms is to make sure
# your user inputs are properly sanitized...
details.user = request.user
# etc
else:
# passing it for the GET part too so the user
# can see the already existing data (if any)
form = AddressForm(instance=address_instance)
return render(request,'showcase/address.html',{'form': form})
Related
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 with radio buttons and text fields. When I submit the form, the boolean field does not get created in the record. The boolean field is supposed to be updated via the radio buttons. What could be the issue here?
Here is the relevant part of my forms.py file:
CHOICES = (
(1,'yes'),
(0,'no')
)
class ServiceForm(forms.ModelForm):
one_time_service = forms.ChoiceField(required = True, choices = CHOICES, widget=forms.RadioSelect())
class Meta:
model = Service
fields = ('one_time_service')
This is my models.py one_time_service field
one_time_service = models.BooleanField(default=False)
This is my views.py:
def create(request):
if request.POST:
form= ServiceForm(request.POST)
if form.is_valid():
service_obj = form.save(commit=False)
service_obj.user_id = request.user.id
service_obj.save()
return render_to_response('services/service_created.html',
{'service': Service.objects.get(id=service_obj.id)})
else:
form = ServiceForm()
args= {}
args.update(csrf(request))
args['form'] = form
return render_to_response('services/create_service.html', args )
Edit: Here is my create_service.html
<form action="/services/create" method="post" enctype="multipart/form-data">{% csrf_token %}
<ul>
{{form.as_p}}
</ul>
<input type="submit" name="submit" value="Create Service">
</form>
I have no idea if this is the problem, but the line:
fields = ('one_time_service')
is wrong. That's not a single element tuple, that's a string with parens around it. Add a comma to make it a tuple:
fields = ('one_time_service',)
Edit: also, form.save() does not update any database records -- it creates a new one! That may be your problem.
first of all, I am new in programming with Django. Here is what I have:
The <form> inside my register.html template:
<form method='POST' action='/accounts/register/'>
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}{{ field }} {{field.help_text}} {{field.errors}{}
<br/>
{% endfor %}
<input type='submit' value='Register' />
</form>
This is inside my forms.py
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required = True)
last_name = forms.CharField(required = True)
password1 = forms.RegexField(widget=forms.PasswordInput,
regex = r'[\w+]{8,}',
label = 'New password',
help_text = "Must be strong!",
error_messages = {'required' : "ASDASDA",
'invalid' : "ZZZZZ"}
)
password2 = forms.RegexField(widget=forms.PasswordInput,
regex = r'[\w+]{8,}',
label = 'Re-enter password',
)
class Meta:
model = User
fields = ('last_name', 'first_name', 'username', 'email', 'password1', 'password2')
def save(self, commit = True):
user = super(MyRegistrationForm, self).save(commit = False)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
if commit:
user.save()
return user
This is inside my views.py
def User_Register_View(request):
if request.method == 'POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
register_success = "Account successfuly created!"
return render(request,'index.html', locals())
args = {}
args.update(csrf(request))
args['form'] = MyRegistrationForm()
return render(request,'accounts/register.html',args)
My questions are the following:
{{field.errors}} is not working. No errors are printed. If I let all the fields of the form empty, and I click 'Register', no error is rendered.
If I add another field to this forum, "gender" as a CharField, will it create a column inside my DB for gender? ( I am working with the default sqlite3)
Is there a simple way to modify my code and make the 'User' field optional and, instead, make the 'Email' field be unique and required?
In the state in which my code is, where can I check for the unique property of my email in the DB? Is it inside my views.py, just before form.save() ?
Thank you. Let me know if you have any questions
def User_Register_View(request):
if request.method == 'POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
register_success = "Account successfuly created!"
return render(request,'index.html', locals())
else:
form = MyRegistrationForm() # An unbound form
args = {}
args['form'] = form
return render(request,'accounts/register.html',args)
{{field.errors}} now showing because you are not returning the validated form instance if validation failed. You always return a new instance of MyRegistrationForm. See above example.
adding fields to forms will not affect your db since only models maps to db tables
& 4. You need custom user model to do so, see here for example on how to do it
Consider a model:
class MyModel(models.Model):
token = models.CharField(unique=True, db_index=True, max_length...)
name = models.CharField(...)
...
(Aside: The purpose of the token is to be an alternative to displaying and using the ID in URLs; it is not the primary key.)
And its form:
class MyForm(forms.ModelForm):
...
class Meta:
model = models.MyModel
fields = '__all__' # Django 1.6
And its template:
...
<form action={% url 'create_or_edit_mymodel' %} ...>{% csrf_token %}
{{ form.token.as_hidden }}
<label for="id_name">Name:</label>
{{ form.name }}
...
And, finally, its view:
def create_or_edit_mymodel(request, token=None):
# [A] Entering via a hyperlink with the token, editing existing model
if token:
m = models.MyModel.objects.filter(token=token).first()
form = forms.MyForm(instance=m)
# [B] Postback from form
elif request.POST:
form = forms.MyForm(request.POST)
# [C] Entering via a hyperlink without the token, creating new model
else:
m = create_new_mymodel(...) # somewhere else
form = forms.MyForm(instance=m)
if request.method == 'POST' and form.is_valid():
saved = form.save()
# Determine if 'Save' or 'Save & Close' was clicked... assume 'Save'...
form = forms.MyForm(instance=saved)
return shortcuts.render(request, '...', { 'form': form }, context_instance=RequestContext(request))
This doesn't work. The problem is that the model's ID doesn't seem to be available to Django, so entering the view at [A] populates the form with everything as expected, but clicking 'Save' and entering the view at [B] attempts to save a model with no ID, and the unique constraint on the 'token' field fires.
I tried adding the id field to the form:
{{ form.id.as_hidden }} # or...
{{ form.pk.as_hidden }}
But nothing gets rendered.
That view looks pretty uncomfortable to me, so I'm hoping I'm making this harder than it needs to be.
Here you should pass both request.POST and instance to form init:
# [B] Postback from form
elif request.POST:
form = forms.MyForm(request.POST, instance=instance)
So I'm building a basic Q&A site-- Each topic has a series of questions associated with it, and each question has multiple answers associated with it.
I'm creating the user input for questions and they have to associated with a topic. This is the questions model
#models.py
class Question(models.Model):
movie = models.ForeignKey(Movie, blank=True, null=True)
question_text = models.CharField(max_length = 1000)
question_detail = models.CharField(max_length = 5000, blank = True, null = True)
q_pub_date = models.DateTimeField(auto_now_add = True)
q_author = models.ForeignKey(User)
class QuestionForm(ModelForm):
def save(self, user = None, force_insert = False, force_update = False, commit = True):
q = super(QuestionForm, self).save(commit = False)
q.q_author = user
if commit:
q.save()
return q
class Meta:
model = Question
exclude = ('movie', 'q_author', 'q_pub_date')
This is the URL conf
#urls.py
url(r'^(?P<movie_id>\d+)/add_question/$', 'add_question'),
Now here is the view
#views.py
def add_question(request, movie_id):
if request.method == "POST":
form = QuestionForm(request.POST, request.FILES)
#QuestionForm.movie = Movie.objects.get(pk = movie_id)
if form.is_valid():
form.save(user = request.user)
return HttpResponseRedirect("/home/")
else:
form = QuestionForm()
return render_to_response("qanda/add_question.html", {'form': form}, context_instance = RequestContext(request))
This is the HTML code
#add_question.html
<h1> Add Question: {{ user.username }}</h1>
<form action = "" method = "post">{% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Ask" />
<input type = "hidden" name = "next" value = "{{ next|escape }}" />
</form>
In the view, the commented out line is what I added to the view to try and auto save the model. When adding a question, the URL has the ID of the movie it is associated with, and my thought is to take that ID and then plug it into the ForeignKey to identify which movie is associated with the question. However, when I use my code, it changes all of the Questions' movie associations to the current movie instead of just changing that specific question's movie association. Without the code, it doesn't associate a Movie with the Question at all. How do I fix this?
Use this:
#views.py
def add_question(request, movie_id):
if request.method == "POST":
form = QuestionForm(request.POST, request.FILES)
if form.is_valid():
question = form.save(user = request.user)
question.movie = Movie.objects.get(pk = movie_id)
question.save()
return HttpResponseRedirect("/home/")
else:
form = QuestionForm()
return render_to_response("qanda/add_question.html", {'form': form}, context_instance = RequestContext(request)
For question asked in comment
You should avoid using absolute URLs in views or templates. Consider a scenario, where you decide to change home URL from /home/ to /myhome/. You will have to edit it where ever you have used them. It is always better to name the urls (docs):
# URL Conf
url(r'^home/$', 'home_view', name="home_url"),
url(r'^(?P<movie_id>\d+)/add_question/$', 'add_question', name="add_question_url"),
url(r'^home/(?P<movie_id>\d+)/$', 'movie_view', name="movie_url"),
The name argument act as an unique identifier to your actual URLs
Now in you views:
from django.core.urlresolvers import reverse
def some_view(request):
...
return HttpResponseRedirect(reverse('home_url'))
Now what ever change you make to the URL (say /home/ to /myhome/ makes no effect to the view as long as the name argument has the same value in the URL conf.
If you wish to pass parameters (like movie_id in your case)
def some_view(request, movie_id):
...
return HttpResponseRedirect(reverse('movie_url', kwargs={'movie_id':movie_id}))
The same concept should be used in templates to avoid hard-coding URLS in templates. Please read this for more details