How to pass data from Django Template to View - python

I have a template where customer details are displayed, along with a 'Create Lead' button at the bottom. This should take the user to the Lead creation form page where the customer field should be pre-selected.
I'm new to django. Based on responses of previous similar questions, I came up with the below code. But when I click the 'Create Lead' button, the url changes from "http://127.0.0.1:8000/sales/customer/21" to "http://127.0.0.1:8000/sales/customer/21/?customer_id=21" but nothing happens on the page. I tried with POST method and csrf token also, but it gives HTTP ERROR 405. Could some please help here.
Also, I've separate view for creating a Lead, which is sort of duplication of CreateView for Lead. And I believe that's not how it's supposed to be. What is the way to club them both in single view.
Below are the code snippets.
Models.py
class Customer(models.Model):
name = models.CharField(max_length=256)
email = models.EmailField(unique=True)
phone = models.PositiveIntegerField()
class Lead(models.Model):
customer = models.ForeignKey(Customer,related_name='Leads',on_delete=models.PROTECT)
destinations = models.CharField(max_length=256)
lead_source = models.CharField(max_length=256,choices=lead_source_choices,default='FNF')
lead_source_id = models.CharField(max_length=25,blank=True)
lead_status = models.CharField(max_length=25,choices=lead_status_choices,default='NEW')
remarks = models.TextField(blank=True)
trip_id = models.CharField(max_length=10,editable=False,unique=True,default="IN"+uuid.uuid1().hex[:5].upper())
creation_date = models.DateField(auto_now=True)
Forms.py
class LeadForm(forms.ModelForm):
class Meta:
model = Lead
fields = ('customer','destinations','lead_source','lead_source_id','lead_status','remarks')
class LeadFromCustomerForm(forms.ModelForm):
class Meta:
model = Lead
fields = ('destinations','lead_source','lead_source_id','lead_status','remarks')
Template
<form method="GET">
<input type="hidden" name="customer_id" value="{{customer.id}}">
<a href="{% url 'SalesApp:lead_create_from_customer' %}">
<button class="btn btn-warning btn-lg float-right" type="submit">Create Lead</button></a>
</form>
Urls.py
path('lead/create/',views.LeadCreateView.as_view(),name='lead_create'),
path('lead/create/customer/',views.LeadCreateFromCustomerView.as_view(),name='lead_create_from_customer')
Views.py
class LeadCreateView(LoginRequiredMixin,UserPassesTestMixin,CreateView):
form_class = LeadForm
model = Lead
def test_func(self):
return self.request.user.groups.filter(name='Sales').exists()
class LeadCreateFromCustomerView(LoginRequiredMixin,UserPassesTestMixin,CreateView):
form_class = LeadFromCustomerForm
model = Lead
def test_func(self):
return self.request.user.groups.filter(name='Sales').exists()
def form_valid(self,form):
customer_id = self.request.GET.get("value")
form.instance.customer = Customer.objects.get(id=customer_id)
return super(LeadCreateFromCustomerView,self).form_valid(form)

Related

Using Django DeleteView and getting a 404 after delete confirmation

After clicking on "confirm" in my organism_delete.html form, I used to be redirected back to the list of organisms (organism_list.html template) as specified in the view. But now I get a 404 error instead.
Page not found (404) Request Method: GET Request
URL: http://localhost:8000/library/organisms/ABC1233/delete/post?csrfmiddlewaretoken=Rdk575IEp5bbvrriJ1szlYNjmq8V1DvuYzNWEWz07s78IJSal9foHdkvxwcimIEp
Using the URLconf defined in itslibrary.urls, Django tried these URL
patterns, in this order:
admin/
accounts/ [name='home']
library/ organisms/ [name='organism_list']
library/ organisms/new/ [name='organism_new']
library/ organisms/ [name='organism_detail']
library/ organisms//update/ [name='organism_update']
library/ organisms//delete/ [name='organism_delete']
^media/(?P.*)$ The current path,
library/organisms/ABC1233/delete/post, didn’t match any of these.
Two things that stand out to me is first that the error says it's a GET request, not a POST as the form specifies.
And second is why is it trying to get to .../delete/post...?
It might be important to know that I changed my model and added "Primary Key = True" to a unique CharField, and I've been modifying the rest of the app to match that. It may not be related because I can list the organisms and I can get to the delete page, I just can't submit it.
I don't know how to debug this, it seems to be hidden behind the Django magic, any guidance will be very appreciated.
Code below:
Models.py:
#Organism
class Organism(models.Model):
genbank = models.CharField(max_length = 10, primary_key=True, unique=True)
genus = models.CharField(max_length = 50)
species = models.CharField(max_length = 50)
strain = models.CharField(max_length = 50)
organism_sequence = models.TextField()
created_at = models.DateTimeField(auto_now_add = True)
fasta = models.FileField(upload_to='organism_fasta/', null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.SET_NULL, null=True)
def __str__(self):
return self.genus[:10] + ', ' + self.species[:10] + ', ' + self.strain[:10]
def get_absolute_url(self):
return reverse('organism_detail', args=[str(self.genbank)])
#Motif
class Motif(models.Model):
organism = models.ForeignKey('Organism', on_delete = models.CASCADE, related_name= "motifs")
region_name = models.CharField(max_length = 15, choices = MOTIF_CHOICES)
motif_sequence = models.CharField(max_length = 600)
structure_image = models.ImageField(upload_to='structures/', blank=True, null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add = True)
def __str__(self):
return self.region_name[:10]
app urls.py
urlpatterns = [
path('organisms/', views.OrganismListView.as_view(), name='organism_list'),
path('organisms/new/', views.OrganismCreateView.as_view(), name='organism_new'),
path('organisms/<pk>', views.OrganismDetailView.as_view(), name='organism_detail'),
path('organisms/<pk>/update/', views.OrganismUpdateView.as_view(), name='organism_update'),
path('organisms/<pk>/delete/', views.OrganismDeleteView.as_view(), name='organism_delete'),
]
Delete View in views.py:
#Delete
class OrganismDeleteView(LoginRequiredMixin, DeleteView):
model = Organism
success_url = '/library/organisms'
template_name = 'library/organism_delete.html'
login_url = "/login"
organism_delete.html template
{% extends "base.html" %}
{% block content %}
<form action="post">
{% csrf_token %}
<p>Are you sure you want to delete {{organism.genus}} {{organism.species}} {{organism.strain}}?</p>
<p>This cannot be undone!</p>
<input type="submit" class="btn btn-danger" value="confirm"/>
</form>
{% endblock content %}
The method="…" of the form is "POST", not the action="…":
<form method="post">
{% csrf_token %}
<p>Are you sure you want to delete {{organism.genus}} {{organism.species}} {{organism.strain}}?</p>
<p>This cannot be undone!</p>
<input type="submit" class="btn btn-danger" value="confirm"/>
</form>
It might be better to work with reverse_lazy(…) [Django-doc] to determine the path of the success_url:
from django.urls import reverse_lazy
class OrganismDeleteView(LoginRequiredMixin, DeleteView):
model = Organism
success_url = reverse_lazy('organism_list')
template_name = 'library/organism_delete.html'
login_url = '/login'
When you do <form action="post" ..., you are asking the form to "append post to the current URL, using a GET request, and go the endpoint with 'your data'".
More details here: https://www.w3schools.com/tags/att_form_action.asp
I am not sure how will you manage to make the page call again the same page (maybe omit action="...", but you should add (change) method="..."; from the implicit method="get", make it a method="post" instead.
Without being a django connoisseur myself, it would be nice if you could "easily" teach django to listen to the method="delete" instead of POST, as it will make your API a tad more semantic.

Django how to connect user profile model with comment model for showing data from user profile?

I want to show user profile picture publicly in my blog comment section. I tried to use foreignkey in my comment model for connect user profile model then use this in my html for showing profile picture but didn't work.
<img src="{{blogcomment.userprofile.profile_pic.url}}"> #didn't show any profile picture until I manually go to admin panel and set foreignkey of userprofile in my blogcomment model.
here is my full code:
userprofile model
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name="userprofile")
slug = models.SlugField(max_length=2000,unique=True,blank=True,null=True)
profile_pic = models.ImageField(upload_to='profile/images/',validators=[validate_file_size,FileExtensionValidator( ['png','jpg'] )],blank=True,null=True)
blogcomment model:
class BlogComment(models.Model):
blog = models.ForeignKey(Blog,on_delete=models.CASCADE,null=True, blank=True,related_name="blogcomment_blog")
comment = models.TextField(max_length=50000)
name = models.CharField(max_length=250)
userprofile= models.ForeignKey(UserProfile,on_delete=models.CASCADE,null=True,blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_comment',blank=True,null=True)
views.py:
if comment_form.is_valid():
isinstance = comment_form.save(commit=False)
isinstance.user = request.user
isinstance.blog = blog
isinstance.save()
my html template:
{% for q in queryset %}
{{q.user.first_name}}
{{q.comment}}
<img src="{{q.userprofile.profile_pic.url}}">
{%endfor%}
my froms.py
class CommentFrom(forms.ModelForm):
captcha = CaptchaField()
class Meta:
model = BlogComment
fields = ['name','email','comment','parent','sno','blog','user']
my userprofile forms.py
class ProfileFroms(forms.ModelForm):
class Meta:
model = UserProfile
fields = ["profile_pic","mobile","country","website_link","skype","twitter"]
userprofile views.py
class UserProfileUpdate(UpdateView):
model = UserProfile
form_class = ProfileFroms
template_name = 'members/profileupdate.html'
success_url = reverse_lazy('members:user-profile-private')
html template for saving userprofile forms
<form method="POST" enctype="multipart/form-data" runat="server">
{% csrf_token %}
{{form}}
</form>
Finally I solved my problems. As Willem Van Onsem said I am missing somethings in my froms. I need to be save userprofile forignkey with my comment model when any new comment posted. I am using this queryset UserProfile.objects.filter(user=request.user) for find current id then pass this id in forms.
{%for i in user_profile%}
<input type="hidden" name='userprofile' value="{{i.id}}">
{%endfor%}

Django access foreignkey fields in a form

I'm working on a Virtual Library app (using Django v2.1, python v3.5) where anyone should be able to access the book catalog and request a loan by simply leaving some personal info like name, surname, email, etc.
These are some of the models in models.py:
class Profile(models.Model):
name = models.CharField(max_length=50)
# more fields like surname, email, phone...
class TrackBook(models.Model):
# Somefields to keep track of date and status...
borrower = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True, blank=True)
class Book(TrackBook):
#info about title, author, etc.
What I'm trying to do is to update a Book instance's borrower with a Profile instance that I created in the Form.
1)I've tried directly accessing borrower fields in a BookForm, but it didn't work.
# views.py
class BookRequestView(UpdateView):
template_name = 'core/book_request.html'
model = Book
form_class = BookProfileForm
#forms.py
class BookProfileForm(forms.ModelForm):
class Meta:
model = Book
fields = ['borrower']
# book_request.html
<form class="" action="" method="post">
{% csrf_token %}
<div class="row">
{{ form.borrower.name }}
<! -- and all other fields -->
</div>
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<button type="submit" class="btn btn-block btn-success btn-flat">Save</button>
</form>
2) I've tried creating an inlineformset_factory() of Profile model but it doesn't work since what I want to achieve is create a profile form inside the book form, not viceversa. So the example here is not what I'm looking for.
Maybe I'm going out of my mind for a very simple thing, but I can't seem to find any compatible solution for this problem... Any help/suggestion is welcome. Thanks in advance.
You need a form based on Profile, not Book. Your view then needs to create the profile and then set the book's borrower to that.
class BookProfileForm(forms.ModelForm):
book = forms.ModelChoiceField(queryset=Book.objects.all())
class Meta:
model = Profile
fields = ['name', 'address',...]
class BookRequestView(CreateView):
template_name = 'core/book_request.html'
model = Book
form_class = BookProfileForm
def form_valid(self, form):
borrower = form.save()
book = Book.objects.get(self.kwargs['book_id'] # or whatever is in your URL
book.borrower = borrower
book.save()
return redirect(self.get_success_url())

How to make django crispy form to hide a particular field?

I'm trying to make my date_modified field as hidden since I have passed datetime.now parameter on defining date_modified field in model.
model.py
class Guide(models.Model):
name = models.CharField(max_length=50)
sno = models.CharField(max_length=50)
date_created = models.DateTimeField(default=datetime.now, blank=True)
date_modified = models.DateTimeField(default=datetime.now, blank=True)
def __unicode__(self):
return unicode(self.name)
views.py
class GuideFormUpdateView(UpdateView):
model = Guide
fields = ['name', 'sno', 'date_modified']
template_name_suffix = '_update_form'
success_url = reverse_lazy('Guides')
corresponding form forms.py looks like
<form role="form" method="POST" action="{% url 'Guideform-edit' object.pk %}"
class="post-form form-horizontal" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy }}
<button type="submit" value="Upload" class="save btn btn-default btn-primary center-block">Update</button>
</form>
This form displays date_modified field. But I don't want this field on frontend instead I want the value of this field in model or db_table should get updated. I know how to hide this particular field in jquery but I don't want to touch those js tools. Is there any way to make crispy to exclude that particular field like {{ form|crispy|exclude:date_modified }} ..
Instead of using Generic Form that your UpdateView will use implicitly, create your custom Form. And in your custom Form change the widget of the date_modified field.
In your forms.py
from django.forms import ModelForm, HiddenInput
class GuideForm(ModelForm):
def __init__(self, *args, **kwargs):
super(GuideForm, self).__init__(*args, **kwargs)
self.fields['date_modified'].widget = HiddenInput()
class Meta:
fields = ('name', 'sno', 'date_modified', )
model = models.Guide
In your views.py
class GuideFormUpdateView(UpdateView):
model = Guide
form_class = forms.GuideForm
template_name_suffix = '_update_form'
success_url = reverse_lazy('Guides')
To automatically update date_modified whenever you update the record, you need to use attributes auto_now and auto_now_add instead of default. See Docs. So your model will be
class Guide(models.Model):
name = models.CharField(max_length=50)
sno = models.CharField(max_length=50)
date_created = models.DateTimeField(auto_now_add=True, blank=True)
date_modified = models.DateTimeField(auto_now=True, blank=True)
def __unicode__(self):
return unicode(self.name)
You can hide a field in a form class like so:
Field('field_name', type="hidden")
Where Field is from crispy_forms.layout
Don't forget that if he field cannot be left empty, you'll still need to pass an appropriate value before saving it.
Abstract example:
class GuideFormHiddenField(GuideFormUpdateView):
def __init__(self, *args, *kwargs):
Field('date_modified', type="hidden")
This is the easiest way. You can of course make an entirely new form, or implement your fields individual, and use a condition to determine the visibility of a certain field; which would be something like if User.is_authenticated(): ... .
I think this should work:
from django.forms.models import modelform_factory
class GuideFormUpdateView(UpdateView):
model = Guide
form_class = modelform_factory(Guide, widgets={"date_modified": HiddenInput })
fields = ['name', 'sno', 'date_modified']
template_name_suffix = '_update_form'
success_url = reverse_lazy('Guides')
See here modelform_factory.

Excluding Fields in Django Class Based Views Not Working

Boy, it feels like I've tried EVERYTHING here, and I just can't get this form to render properly (e.g. with a subset of fields). Here's the relevant items (extra items removed):
models.py:
class Response(models.Model):
public = models.BooleanField(default=False)
question = models.CharField(max_length=255, default='', blank=True)
class ResponseForm(ModelForm):
class Meta:
model = Response
fields = ['question']
views.py:
class ResponseCreate(CreateView):
model = Response
fields = ['question']
response_form.html:
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Create" />
</form>
I've read every bit of docs, and they suggest using exclude, excludes, field, fields in permutations of tuple, or list, and no matter what the "public" field seems to show up every time! Thoughts?
Thanks for your help!
It looks like you need to specify form_class on your view:
class ResponseCreate(CreateView):
model = Response
form_class = ResponseForm
Just I want to mention that you don't really need a ModelForm to be created separately.
Generic views really shine when working with models. These generic
views will automatically create aModelForm, so long as they can work
out which model class to use
You can just use this.
class Response(models.Model):
public = models.BooleanField(default=False)
question = models.CharField(max_length=255, default='', blank=True)
class ResponseCreate(CreateView):
model = Response
fields = ['question']
You can use exclude field in your class form:
class ResponseForm(ModelForm):
class Meta:
model = Response
exclude = ['field_to_exclude']
class ResponseCreate(CreateView):
model = Response
form_class = ResponseForm

Categories