I have googled several hours but couldn't understand how to write sql join(raw or ORM) related queries.
Below is my model with two tables sandBox1 and licenseType where they will have common item "email" on which join will be performed
class sandBox1(models.Model):
email = models.EmailField(unique=True)
name = models.CharField(max_length=200)
website = models.TextField(validators=[URLValidator()])
comment = models.TextField(default='-')
gender = models.CharField(max_length=6)
def __str__(self):
return self.email
class licenseType(models.Model):
#1=other, 2=two-wheeler 4=four-wheeler
licenseId = models.IntegerField()
email = models.EmailField()
template file : index.html
<html><form id="form1" method="post" action="{% url "sandbox" %}">
{% csrf_token %}
Name: <input type="text" name="name" >
<br><br>
E-mail: <input type="text" name="email">
<br><br>
Website: <input type="text" name="website" >
<span class="error"></span>
<br><br>
Comment: <textarea name="comment" rows="5" cols="40"></textarea>
<br><br>
Gender:
<input type="radio" name="gender" value="female">Female
<input type="radio" name="gender" value="male">Male
<hr>Check the license type you have:-<br>
<input type="checkbox" name="license[]" value=2 > 2 wheeler<br>
<input type="checkbox" name="license[]" value=4 > 4 wheeler<br>
<input type="checkbox" name="license[]" value=1 > Other <br>
<br>
<input type="submit" name="submit" value="Submit">
</form>
<div>
{% for obj in sandBoxObj %}
<p>
{{ obj.name }}<br>
{{ obj.email }}<br>
{{ obj.website }}<br>
{{ obj.gender }}<br>
{{ obj.comment }}<br>
{% endfor %}
</div>
</html>
here is a view file that needs correction. I want to show the result of this sql query:
select sandBox1.email,sandBox1.name,licenseType.licenseId from sandBox1
innerjoin licenseType on sandBox1.email=licenseType.email;
View file
def sandbox(request):
template_name='domdom.html'
sandBoxObj = sandBox1.objects.all()
context = { 'sandBoxObj':sandBoxObj }
print request.POST
if request.method == 'POST':
website=request.POST.get('website','')
comment=request.POST.get('comment','')
name=request.POST.get('name','')
gender=request.POST.get('gender','')
email=request.POST.get('email', '')
license=request.POST.getlist('license[]')
for id in license:
licInst = licenseType(licenseId=id,email=email)
licInst.save()
sbinstance = sandBox1(website=website,comment=comment,name=name,gender=gender,email=email)
sbinstance.save()
return render(request,template_name,context)
Raw sql method/ but im still confused on ORM method
def sandbox(request):
template_name='domdom.html'
sandBoxObj = sandBox1.objects.all()
con = sqlite3.connect('/home/user1/PycharmProjects/djrest/invoicesproject/db.sqlite3') #sqlite database file location
cursor = con.cursor()
cursor.execute(''' select todos_sandBox1.email,todos_sandBox1.name,todos_sandBox1.website,todos_sandBox1.comment,todos_sandBox1.gender,todos_licenseType.licenseId from todos_sandBox1
join todos_licenseType on todos_sandBox1.email=todos_licenseType.email
''') #it looks like django appends app name to table eg. appname = todos
result = cursor.fetchall()
#https://www.youtube.com/watch?v=VZMiDEUL0II
context = { 'result':result }
print request.POST
if request.method == 'POST':
website=request.POST.get('website','')
comment=request.POST.get('comment','')
name=request.POST.get('name','')
gender=request.POST.get('gender','')
email=request.POST.get('email', '')
license=request.POST.getlist('license[]')
for id in license:
licInst = licenseType(licenseId=id,email=email)
licInst.save()
sbinstance = sandBox1(website=website,comment=comment,name=name,gender=gender,email=email)
sbinstance.save()
return render(request,template_name,context)
Sorry if this answers the wrong question, but you may want to consider a different data model/ architecture. You are hardcoding SANDBOX1 which implies that there might be multiple Sandboxes and you are listing email fields which aren't tied to the User object. Some basic abstractions may simplify the work. Maybe something like:
from django.contrib.auth.models import User
...
class LicenseTypes(models.Model):
name = models.CharField(max_length=500)
class Customer(models.Model):
name = models.CharField(max_length=500)
license = models.ForeignKey(LicenseType)
class RegisteredUser(models.Model):
customer = models.ForeignKey(Customer, on_delete = models.CASCADE)
user = models.ForeignKey(User)
I like this architecture better because it uses a lot more native django functionality. And makes joins really basic. Check this out in a view:
def django_view(request):
registered_user = RegisteredUser(user=request.user)
#example of how to use the join implicitly/ directly
license = registered_user.customer.license.name
Related
My website has a comment section where user can leave comments on a product . The comments on the product page will be stored in a model called 'ProductReview'. Here is the code for the model :
class ProductReview(models.Model):
product = models.ForeignKey(Product, related_name='reviews', on_delete=models.CASCADE)
name = models.CharField(blank=True,max_length=20)
stars = models.IntegerField()
content = models.TextField(blank=True)
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.OneToOneField(User, on_delete=models.CASCADE)
Now the view associated with the model are as follows Note:The entire view isnt relevant to the error. The part relevant to the saving comment is the second 'request.POST' which I have denoted with a python
comment using # :
def product(request, category_slug, product_slug):
cart = Cart(request)
product = get_object_or_404(Product, category__slug=category_slug, slug=product_slug)
if request.method == 'POST':
form = AddToCartForm(request.POST)
if form.is_valid():
quantity = form.cleaned_data['quantity']
cart.add(product_id=product.id, quantity=quantity, update_quantity=False)
messages.success(request, 'The product was added to the cart')
return redirect('product', category_slug=category_slug, product_slug=product_slug)
similar_products = list(product.category.products.exclude(id=product.id))
# this part is for saving of the user comments to productreview model
if request.method == 'POST':
stars = request.POST.get('stars', 3)
content = request.POST.get('content', '')
name = request.POST.get('name', '')
created_by = request.user
review = ProductReview.objects.create(product=product, name=name, stars=stars, content=content, created_by=created_by)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
# this marks the end of the code relevant to saving the user comment
if len(similar_products) >= 4:
similar_products = random.sample(similar_products, 4)
user_type = 0
if request.user.is_authenticated:
user_type = UserType.objects.filter(created_by=request.user.id).values_list('user_type', flat=True)
user_type = int(user_type[0])
return render(request, 'product.html', {'product': product, 'similar_products': similar_products, 'user_type': user_type})
And finally the relevant part of the template 'product.html' which is referenced in the view
{% if request.user.is_authenticated %}
{% if user_type == 2 %}
<div class="notification space-below">
<form method="post" action=".">
{% csrf_token %}
<div class="field">
<label>Name</label>
<div class="control">
<input class="text" name="name" value="{{ request.user }}" readonly>
</div>
</div>
<div class="field">
<label>Stars</label>
<div class="control">
<div class="select">
<select name="stars">
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected>3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
</div>
</div>
<div class="field">
<label>Content</label>
<div class="control">
<textarea class="textarea" name="content"></textarea>
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-success">Submit</button>
</div>
</div>
</form>
</div>
{% else %}
<div>sign in with a buyer account to leave review</div>
{% endif %}
{% endif %}
Now when I try to fill the form in the product.html page and try to submit it ,I get the following error:
FOREIGN KEY constraint failed
IntegrityError at /smartwatch/apple-watch/
Can anyone tell what exactly is the issue with my code?
Pretty sure this all comes to your model, especially to this single line:
created_by = models.OneToOneField(User, on_delete=models.CASCADE)
You are using OneToOneField. This means a single user will be able to leave a single review in your entire application. You are getting integrity errors probably because this user you've selected already has a review on a different product.
If this isn't what you want, use normal ForeignKey instead.
I think your intention was to ensure that a single user can leave only one review per product. In that case, you should try setting UniqueConstraint between the product and created_by fields
'''Models Code'''
# Product Model
class Products(models.Model):
name = models.CharField(max_length=50)
img = models.ImageField(upload_to='productImage')
CATEGORY = (
('Snacks','Snacks'),
('Juice','Juice'),
)
category = models.CharField(max_length=50, choices=CATEGORY)
description = models.TextField()
price = models.FloatField()
# Rating Model
class Rating(models.Model):
product = models.ForeignKey(Products, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
stars = models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)], blank=True, null=True)
comment = models.TextField(blank=True,null=True)
''' Views Code '''
class ProductListView(ListView):
model = Products
template_name = 'products.html'
context_object_name ='Products'
class ProductDetailView(LoginRequiredMixin,DetailView):
login_url = '/accounts/login'
model = Products
# Using this function I want to take the rating and comment, but how can I access the cuurent object for which the comment and rating is being send by the user.
def review(request,slug):
star=request.POST.get('rating')
comment=request.POST.get('comment')
user = request.user
productId = request.POST.get('productsid') # How to get the Product
product = Products.objects.get(id=productId)
review = Rating(product=product,user=user,stars=star,comment=comment)
review.save()
return redirect('/')
# Urls code
urlpatterns = [
path('',views.home,name='Home'),
path('products',ProductListView.as_view(),name='Products'),
path('product/<int:pk>',ProductDetailView.as_view(),name='Product-Details'),
path('contact',views.contact,name='Contact'),
path('review',views.review,name='review')
#Templates Code
<form method="POST" action="review">
{% csrf_token %}
<input type="hidden" id="rating-value" name="rating">
<textarea style="margin-top:5px;" class="form-control" rows="3" id="comment" placeholder="Enter your review" name="comment"></textarea>
<button type="submit" style="margin-top:10px;margin-left:5px;" class="btn btn-lg btn-success">Submit</button>
</form>
How to fetch the current object from the deatailed view page in the review function?
I have added the code here. In Product detailed view page it is rendering the page through which I want to take rating and comment for the product . Is there any other way through which I can get the product, user , star, and rating field value and store it in the data base?
I can point out some ways to retrieve the product_id in your review function.
First approach:
You can pass the product_id as a URL parameter. In this case, I hope the review view is called from the product detail page.
So, your url should be something like:
path('review/<int:product_id>', views.review, name="review),
Your view:
def review(request, *args, **kwargs):
star=request.POST.get('rating')
comment=request.POST.get('comment')
user = request.user
productId = kwargs.get('product_id') # change is here
product = Products.objects.get(id=productId)
review = Rating(product=product,user=user,stars=star,comment=comment)
review.save()
return redirect('/')
Your template:
<form method="POST" action="{% url 'review' object.pk %}">
{% csrf_token %}
<input type="hidden" id="rating-value" name="rating">
<textarea style="margin-top:5px;" class="form-control" rows="3" id="comment" placeholder="Enter your review" name="comment"></textarea>
<button type="submit" style="margin-top:10px;margin-left:5px;" class="btn btn-lg btn-success">Submit</button>
</form>
In the template, the object is the object_name you have given to the product object. You can change the object name by adding:
context_object_name = product
in your ProductDetailView.
Second approach:
Pass the product_id as a form data. You can create a hidden input in your template that will contain the product_id as value. For example:
In your template:
<form method="POST" action="review">
{% csrf_token %}
<input type="hidden" id="rating-value" name="rating">
<input type="hidden" name="product_id" value="{{ object.pk }}"> # add a hidden input field
<textarea style="margin-top:5px;" class="form-control" rows="3" id="comment" placeholder="Enter your review" name="comment"></textarea>
<button type="submit" style="margin-top:10px;margin-left:5px;" class="btn btn-lg btn-success">Submit</button>
</form>
Where object is what I mentioned previously.
Then you can retrieve the product_id in view as:
def review(request,slug):
star=request.POST.get('rating')
comment=request.POST.get('comment')
user = request.user
productId = int(request.POST.get('product_id')) # here
product = Products.objects.get(id=productId)
review = Rating(product=product,user=user,stars=star,comment=comment)
review.save()
return redirect('/')
how can i save many courses to the student table .I want to keep my design like this.This code is not saving the many to many field(courses) through AddStudentForm.It returns an error with courses variable.If i used CharField instead of ManyToManyField in models for courses then the code works perfectly,but when i use ManyToManyField then it is not working.
it throws courses when i used form.errors .If i didn't use form.errors then it doesn't give any error neither saves the data.
how can i save many courses to the student table .I want to keep my design like this.This code is not saving the many to many field(courses) through AddStudentForm.It returns an error with courses variable.
models.py
class Course(models.Model):
title = models.CharField(max_length=250)
price = models.IntegerField(default=0)
duration = models.CharField(max_length=50)
def __str__(self):
return self.title
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course)
email = models.EmailField()
image = models.ImageField(upload_to='Students',blank=True)
def __str__(self):
return self.name
forms.py
class AddStudentForm(forms.ModelForm):
# courses = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Course.objects.all())
class Meta:
model = Student
fields = ['name','courses','email','image']
def __init__(self, *args, **kwargs):
super(AddStudentForm, self).__init__(*args, **kwargs)
self.fields["courses"].widget = CheckboxSelectMultiple()
self.fields["courses"].queryset = Course.objects.all()
views.py
def addstudent(request):
courses = Course.objects.all()
if request.method == 'POST':
form = AddStudentForm(request.POST,request.FILES)
if form.is_valid():
student = form.save(commit=False)
course = form.cleaned_data['courses']
student.courses = course
student.save()
# student.courses.add(course)
# student.save_m2m()
# student.courses.set(course) # this method also didn't helped me
messages.success(request, 'student with name {} added.'.format(student.name))
return redirect('students:add_student')
else:
# messages.error(request,'Error in form.Try again')
return HttpResponse(form.errors) # this block is called and returns courses
else:
form = AddStudentForm()
return render(request,'students/add_student.html',{'form':form,'courses':courses})
add_student.html
<form action="{% url 'students:add_student' %}"
method="post"
enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<h5>Full Name <span class="text-danger">*</span>
</h5>
<div class="controls">
<input type="text" name="name" class="form-
control" > </div>
</div>
<div class="form-group">
<h5>Courses<span class="text-danger">*</span>
</h5>
<div class="controls">
{% for course in courses %}
<input name ="courses" type="checkbox" id="course-
{{course.id}}" value="{{course.title}}">
<label for="course-{{course.id}}">{{course.title}}
</label>
{% endfor %} # i think the problem is here.
</div>
</div>
<div class="form-group">
<h5>Email <span class="text-danger">*</span></h5>
<div class="controls">
<input type="text" name="email" class="form-
control" required> </div>
</div>
</div>
<div class="form-group">
<h5>Image <span class="text-danger">*</span></h5>
<div class="controls">
<input type="file" name="image" class="form-control" > </div>
</div>
<div class="text-xs-right">
<button type="submit" class="btn btn-info">Add</button>
</div>
</form>
You need to save first before you can assign m2m, the system needs the primary key of the Student model before it can insert into the m2m table.
if form.is_valid():
student = form.save(commit=False)
course = form.cleaned_data['courses']
student.save()
# this will save by itself
student.courses.set(course)
This is my model:
class Profile(models.Model):
date = models.DateTimeField(auto_now_add=True)
full_name = models.CharField(max_length=32,blank=True)
user_types = (
('Business User','Business User'),
('Professional','Professional'),
)
user_type = models.CharField(max_length=32,choices=user_types,default='Business User',blank=False)
user= models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
e_mail = models.EmailField(max_length=70,blank=True)
As you can see the are two types of user in my Profile model Business User and Professional. So I want to make a query in django which will search only for the Professional users.
I have tried this in my views:
query = request.GET.get('q')
if query:
if Profile.objects.filter(user_type__icontains='Professional'):
result = Profile.objects.filter(Q(user__username__icontains=query) | Q(e_mail__icontains=query) | Q(full_name__icontains=query))
else:
result = Profile.objects.filter(user_type__icontains='Professional').order_by('id')
In my template:
<form method='GET' class="form-horizontal" id="search-form" action="{% url 'userprofile:search' %}">
<div class="col-lg-8 col-md-8 col-sm-8 col-xs-6">
<input class="form-control" name="q" value="{{request.GET.q}}" placeholder="Search">
</div>
<div >
<span class="input-group-btn">
<button type="submit" class="btn btn-info pull-right">Go</button>
</span>
</div>
</form>
But when I search for a Professional user it gives me list of all the user associated with that name.
Can anyone provide me with suitable solution for this.
Thank you
Solved It
Done the following in my view and it worked:
user_profile = Profile.objects.filter(user_type__icontains='Professional')
query = request.GET.get('q')
if query:
result = user_profile.filter(Q(user__username__icontains=query) | Q(e_mail__icontains=query) | Q(full_name__icontains=query))
else:
result = Profile.objects.filter(user_type__icontains='Professional').order_by('id')
I have created a view that should update some data about a user in the database. Earlier I had issues with populating the page, but with 'user7485741' I played around with the data until I was able to populate the form how I wanted to. However, when I hit submit the database is wiped clean of data. I mean if I have a price of '$2.00' that I set in the database and then in the html page I change it to '$3.00', the database shows $0.00 in the backend. Indeed, I see that print(self.cleaned_data) returns {'user': <User: a.a#a.com>}. It isn't capturing any other fields.
Thanks!
Models.py
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
price = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
travel_flag = models.BooleanField(blank=True, default = False)
forms.py
class Meta:
model = Person
fields = ['user', 'travel_flag','price']
views.py
class UserProfileUpdateView(LoginRequiredMixin, UpdateView):
form_class = UserProfileChangeForm
template_name = 'accounts/profile-update-view.html'
def get_object(self):
qs = Person.objects.filter(pk=self.request.user.person.user_id).first()
return qs
def get_context_data(self, *args, **kwargs):
context = super(UserProfileUpdateView, self).get_context_data(*args, **kwargs)
context['title'] = 'Update Your Profile'
return context
def get_success_url(self):
return reverse("account:home")
html page
<form method='POST' action='{% if action_url %}{{ action_url }}{% endif %}'> {% csrf_token %}
<div class="form-group">
<label for="id_price" class="active">Price: $</label>
<input id="id_price" type="number" name="currency" min="0" max="99999" step="0.01" size=4 value="{{ user.person.price }}">
</div>
<div class="form-check">
<input id="id_travel" type="checkbox" name="travel" value="yes" {% if user.person.travel_flag %}checked{% endif %}>
<label for="id_travel" class="active"></label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
I figured it out. Turns out if your cleaned_form isn't passing back data, it could be because the form is not setting that field. In my case above I have
<input id="id_price" type="number" name="currency" min="0" max="99999" step="0.01" size=4 value="{{ user.person.price }}">
when I should have
<input id="id_price" type="number" name="price" min="0" max="99999" step="0.01" size=4 value="{{ user.person.price }}">
</div>
The name on an html page corresponds to the field in the database that you are updating.