validate datetime input uniqueness in CreateView form - python

I have this model, and what I need is the time to be unique as it's a Reservation, so when I create a new reservation, how can I check if that time is already picked.
models.py
class Reserva(models.Model):
horario = models.DateTimeField(auto_now_add=False, auto_now=False)
cancha = models.ForeignKey('Cancha', on_delete=models.CASCADE)
lote = models.IntegerField()
views.py
class ReservasCreateView(generic.edit.CreateView):
model = Reserva
template_name = 'reservar.html'
fields = ['horario', 'cancha', 'lote']
reservation.html
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Reservar">
</form>
And another doubt I have, if I want to change some input types in the form, should I keep the default CreateView and modify it over the view, or create a custom form in forms.py and pass it as form_class. What's the recomendation? Thanks

You can force the creation of reservations's date to be unique in the database level by adding:
horario = models.DateTimeField(unique=True)
Or, you can override the form_valid() method in order to check if the date is picked or not and rise an exception or whatever you want:
from django.contrib import messages # better than raising an exception
from app_name import models # import your APP models
class ReservasCreateView(generic.edit.CreateView):
model = Reserva
template_name = 'reservar.html'
fields = ['horario', 'cancha', 'lote']
success_url = 'YOUR_SUCCESS_URL'
def form_valid(self, form):
date = form.cleaned_data.get('horario', None)
# check if the date is unique
# If we find the same date stored in the database
# we'll add a message to be rendered into the template
dates = models.Reserva.objects.filter(horario=date).first()
if dates:
messages.error(self.request, "This current date is not unique!")
return super().form_valid(form)
And then in your template you can add the messages framework like this example:
{% for message in messages %}
{{ message }}
{% endfor %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Reservar">
</form>
For more information you can visit the django official documentation

Related

How to pass a argument (from a Form) to a Model method and return calculation

I'm learning Python/Django and I have some trouble understanding how you call/pass argument into Models methods
In my case I take a Decimal input from a Form, pass it to a my model method, and recalculate a new value based on data the Model already has in the Sqlite3 db
and display this to my template
If I remove the variable argument in model method and multiply by a constant directly in the model method, the code works and renders fine in the template
Models:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.name
def n_price(self, newprice):
n_price = self.price * newprice
return n_price
Form:
from django import forms
class MultiForm(forms.Form):
data = forms.DecimalField(max_digits=5, decimal_places=2)
Views:
from django.shortcuts import render
from .models import Product
from .forms import MultiForm
def multi(request):
list = Product.objects.all()
if request.method == "POST":
form = MultiForm(request.POST or None)
if form.is_valid():
data = request.POST.get('model')
p = Product.n_price()
p(data)
return render(request, 'viewinventory/multi.html', {'form': form, 'list': list, 'data': data})
Template
...```
{% block content %}
<h2>New price</h2>
<form method="POST" class="price-form">
{% csrf_token %}
{{ form }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}
{% for list in list %}
<div>
{{ list.name }} {{ list.price }} {{ list.n_price }} </div>
{% endfor %}
...```
You are passing form (not a number) to the function which is causing the issue. You need to pass data so change the line Product.n_price(form) to Product.n_price(form.data) to get the number data from the Namespace form.

Django adding to database with foreign key while still showing the informations from the other model

I want to add elements to my database (for model Student) while having stuff from another model (School) be displayed alongside the form for the Student.\
This is the models.py
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def get_absolute_url(self):
return reverse('basiccbv:detail', kwargs={'pk':self.pk})
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField(validators= [validators.MinValueValidator(1),validators.MaxValueValidator(20)],default=1)
school = models.ForeignKey(School, related_name='students')
def __str__(self):
return self.name
In my views.py I have this:
class SchoolDetailedView(DetailView):
context_object_name = 'school_detail'
model = models.School
template_name = 'basiccbv/school_detail.html'
# What i want is when I visit the link in the description I want to
# to see the school stuff and the form to add the student in this new
# view
class StudentCreateView(CreateView):
model = models.School
# I tried using the Student but that I don't know how to display the
# school information, I tried with related_name = 'students' but it
# didn't work(I don't know if that can be done the way that intended it
# or I just don't have the knowledge )
fields = ['name', 'age']
# If I use School I could get the name of the school in the title and
# its primary key, but than I don't know how to display the form and
# vise versa
template_name = 'basiccbv/student_update.html'
This is the .html file that gets me to the page where I need the form.
The link is the one calling 'basiccbv:studentupdate'
The related_name students was used here but I still can't figure out if it can
be done for adding stuff the way I want
<h1>Welcome to the school details page</h1>
<h2>School details:</h2>
<p>Name: {{ school_detail.name }}</p>
<p>Principal: {{ school_detail.principal }}</p>
<p>Location: {{ school_detail.location }}</p>
<h3>Students:</h3>
{% for student in school_detail.students.all %}
<p>{{ student.name }} who is {{ student.age }} years old.</p>
{% endfor %}
<div class="container">
<p><a href="{% url 'basiccbv:studentupdate' pk=school_detail.pk %}">Add a
student</a></p>
And here is the .html file with the form
## I changed this part bellow but nothing worked
<h1>Student coming to {{ student.school.name }}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Add student">
</form>
I'm really stuck and can't find any information about this but if you can help me or give any advice thank you.
The way I used to add students was with admin and for schools I used admin until I created the view for creating Schools which worked with no problems(probably because there were no foreign keys).
I think you can take this approach
Forms:
# We need to define a new form first
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ['name', 'age']
Views:
# we are using form view for using the form mentioned above
class StudentCreateView(FormView):
form_class = StudentForm
success_url = "/"
def get(self, request, school_id, **kwargs):
context = self.get_context_data(**kwargs) # getting context, ie: the form
context[school] = School.objects.get(pk=school_id) # updating the context with school object using the PK provided with the url
return self.render_to_response(context)
def post(self, request, school_id, **kwargs):
# overriding default implementation
form = self.get_form()
if form.is_valid():
return self.form_valid(form, school_id) # passing the pk value to form valid function to override
else:
return self.form_invalid(form)
def form_valid(self, form, school_id):
# overriding default implementation
self.object = form.save(commit=False)
self.object.school = School.objects.get(id=school_id) # saving the school information to the object
self.object.save()
return super(StudentCreateView, self).form_valid(form)
Template
# template
<h1>Student coming to {{ school.name }}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Add student">
</form>
Urls
path('school/student-update/<int:school_id>/', StudentCreateView.as_view(), name='studentupdate'),

How to dynamically update form data in django

I am new to django and I am trying to create a review system, whereby each team member reviews all the other members within their team.
Here is my models.py file:
from django.db import models
from django.contrib.auth.models import User
class Team(models.Model):
name = models.CharField(max_length=25)
def __str__(self):
return self.name
class Trait(models.Model):
name = models.CharField(max_length=25)
def __str__(self):
return self.name
class Review(models.Model):
reviewer = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='reviewer_id')
reviewee = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='reviewee_id')
trait = models.ForeignKey(Trait, on_delete=models.CASCADE)
trait_score = models.IntegerField()` return
This is my views.py file:
from django.shortcuts import render, redirect
from review.forms import ReviewForm
from django.http import HttpResponse
from django.contrib.auth.models import User
from accounts.models import UserProfile
def positivity_review(request):
if request.method == 'POST':
form = ReviewForm(request.POST)
if form.is_valid():
form.save()
return redirect('/review/relationships')
else:
form = ReviewForm()
users = UserProfile.objects.filter(review_team=1)
args = {'form': form, 'team_members': users}
return render(request, 'review/positivity.html', args)` return
This is my forms.py file:
from django import forms
from django.forms.widgets import NumberInput
from review.models import Team, Review
class RangeInput(NumberInput):
input_type = 'range'
class ReviewForm(forms.ModelForm):
trait_score = forms.IntegerField(widget=RangeInput, min_value=0,
max_value=100, label='')
class Meta:
model = Review
fields = (
'trait_score',
)` return
This is the HTML file:
{% extends 'base.html' %}
{% block head %}
<title>Review</title>
{% endblock %}
{% block content %}
<br>
<h1>Review</h1>
<h2>Do they foster a postive climate?</h2>
<div class="row">
<div class="col-md-2">
<p>Exhibits a lack of awareness for a positive climate. Resistance to prompting.</p>
</div>
<div class="col-md-2">
<p>Cooperates at times, within structured activities and friendly under prompting.</p>
</div>
<div class="col-md-2">
<p>Cooperates within the team environment without prompting.</p>
</div>
<div class="col-md-2">
<p>Cooperates well with others, enthusiastic and positve. Occationally prompts others to engage positively.</p>
</div>
<div class="col-md-4">
<p>Seeks to continuously and consistently create a positive environment. Acts as a role model for the team through prompting being supportive and encouraging and showing genuine concern for others.</p>
</div>
</div>
<div>
<form method="post">
{% for user in team_members %}
<p>Reviewing: {{ user.first_name }} {{ user.last_name }}</p>
{% csrf_token %}
{{ form.as_p }}
{% endfor %}
<button class="btn btn-primary" type="submit">Next</button>
</form>
</div>
{% endblock %}`
Currently I am passing in the queryset through the views.py into the html file and looping through it to load the relevant number of team members.
Since I am loading a form each time for each individual in the team, how can I make the form submit so that it knows who is being reviewed? For example, submitting the reviewer, trait and score is simple as most of that can be passed directly into the view, however, submitting the reviewee (person being reviewed) is the part im not sure how to handle, as they are loaded within the form using the template tagging. I was wondering if it is possible to submit some kind of data back into the form such as first + last name or thier user id, anything so that when I go to publish the results I have a way of filtering individuals results.
Hopefully the description is sufficient. Thanks in advance!
If I understand your question correctly, this might be the answer.
First, create a simple form (rather than ModelForm) and add this, among other things:
pk = forms.CharField(
widget=forms.TextInput(
attrs={
'type': 'hidden',
}
),
label=''
)
This will hold the pk of the reviewee and won't be visible to reviewer.
Then, in the html file, I think you have to generate a separate form for each user rather than only the input (I'm not sure, try it). You can do this:
{% for user in team_members %}
<form method="post" id="review_form_{{user.pk}}">
<p>Reviewing: {{ user.first_name }} {{ user.last_name }}</p>
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary" type="submit">Next</button>
</form>
<script type="text/javascript">
$('#review_form_{{user.pk}}').children('#id_pk').val("{{user.pk}}");
</script>
{% endfor %}
Remember that when django generates a form, each input will have to get an id and django adds id_ to the beginning of the name of the field you create in fields.py
And lastly, when the reviewer submit a form, you can find your reveiwee in views.py this way:
form = ReviewForm(request.POST)
if form.is_valid():
reviewee_id = request.POST.get('pk')
reviewee = User.objects.get(pk=reviewee_id)

Django CBV form using list of objects

I am trying to create a form that is a list of cars with one field being a BooleanField. I want this to appear as a form with the BooleanField being a checkbox. If the user checks this, then the BooleanField will be set = True and something will happen when a POST occurs and the user is redirected to the next page.
model.py:
class Car(models.Model):
year = models.IntegerField()
make = models.CharField(max_length=30)
model = models.CharField(max_length=30)
send = models.BooleanField(default=False)
currenly the email.html looks like this:
<form action="" method="post">{% csrf_token %}
{% for car in object_list %}
<input type="checkbox" name="car" id="car{{ forloop.counter }}" value="{{ car.id }}">
<label for="car{{ forloop.counter }}">{{ car.year }} {{ car.make }} {{ car.model }}</label><br>
{% endfor %}
<input type="submit" value="Preview">
</form>
views.py
class Email(ListView):
model = Car
template_name = 'cars/email.html'
Suggestions?
I ended up adding a post() function to the ListView which processes the form data, but I am having trouble redirecting without a valid HttpResponse object and am getting an error when one of the boxes isn't checked in the form. Here is the additional post() code that I added for the time being:
def post(self, request, *args, **kwargs):
cars = Car.objects.all() # initially reset "self.send" field == False
for i in range(len(cars)):
cars[i].send = False
cars[i].save()
cars = Car.objects.filter(id__in=request.POST.getlist('car'))
for i in cars:
i.send = True
i.save()
return HttpResponseRedirect(reverse('cars:email_preview'))
Any suggestions on how to make the form re-render with an error msg if no boxes are checked?
Thanks

Saving data using Django ModelForms

I used this code previously it worked fine and i was suggested to use ModelForm by another member, it did make sense to use the form.is_valid() function etc.. so thought of giving it a try.
I went through some other examples on the internet but mine does not seem to work for some reason, or may be I am not doing it right, I get the following when I print the form in the view, and it goes to the else statement, so my form does not get saved
<input id="id_product" type="text" name="product" value="aassddf" maxlength="250" />
FAIL
My model.py
from django.db import models
from django.forms import ModelForm
class Category(models.Model):
name = models.CharField(max_length=250)
def __unicode__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category)
product = models.CharField(max_length=250)
quantity = models.IntegerField(default=0)
price = models.FloatField(default=0.0)
def __unicode__(self):
return self.product
class ProductForm(ModelForm):
class Meta:
model = Product
My views.py
from models import *
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
def index(request):
...
...
def add_product(request):
if request.method == 'POST':
form = ProductForm(request.POST)
print form['product']
if form.is_valid():
form.save()
return HttpResponseRedirect('/product')
else:
print 'FAIL'
return HttpResponseRedirect('/product')
My html
<form method="post" action="add_product/">
{% csrf_token %}
<label for="category">Category</label>
<select name="category" id="category">
{% for category in category_list %}
<option> {{ category.name }} </option>
{% endfor %}
</select>
<label for="product">Product</label>
<input type="text" name="product" id="product">
<label for="quantity">Quantitiy</label>
<input type="text" name="quantity" id="quantity">
<label for="price">Price</label>
<input type="text" name="price" id="price">
<input type="submit" value="Add New product" id="create">
</form>
Is there a better way i could save the data, using ModelForms ??
Thanks in advance for the help.
You should read the documentation. If the form is not valid, it will have a whole set of errors associated with it, which will tell you exactly why. But you just throw that away, and redirect to /product. The docs show exactly how to redisplay the form with the errors.
Also you should not write HTML form field tags directly in your template: use the form object from the view - {{ form.product }}, etc - as these will be repopulated with the appropriate values on redisplay.
Thanks to Daniel Roseman and Anuj Gupta I think I finally re-worked on my code on got it working in a standard way so it will generate the html form and validate errors.
So for anyone else who is trying to work django forms here is the code I worked on.
My model.py is was almost the same one i posted on the question but i removed
class ProductForm(ModelForm):
class Meta:
model = Product
I created a new form.py here is the code-
from django import forms
from models import Category
class ProductForm(forms.Form):
# Put all my Categories into a select option
category = forms.ModelChoiceField(queryset=Category.objects.all())
product = forms.CharField()
quantity = forms.IntegerField()
price = forms.FloatField()
My views.py changed had a lot of changes -
def add_product(request):
success = False
if request.method == "POST":
product_form = ProductForm(request.POST)
if product_form.is_valid():
success = True
category = Category.objects.get(name=product_form.cleaned_data['category'])
product = product_form.cleaned_data['product']
quantity = product_form.cleaned_data['quantity']
price = product_form.cleaned_data['price']
new_product = Product(category = category, product = product, quantity = quantity, price = price )
new_product.save()
new_product_form = ProductForm()
ctx2 = {'success':success, 'product_form':new_product_form}
return render_to_response('product/add_product.html', ctx2 , context_instance=RequestContext(request))
else:
product_form = ProductForm()
ctx = {'product_form':product_form}
return render_to_response('product/add_product.html', ctx , context_instance=RequestContext(request))
Finally in my html page i used {{ product_form.as_p }} so it created the forms dynamically
{% if success %}
<h3> product added successfully </h3>
{% endif %}
<form method="post" action=".">
{% csrf_token %}
{{ product_form.as_p }}
<input type="submit" value="Add New product" id="create">
<input type="reset" value="reset" id="reset">
</form>
This may not be the perfect solution, but for a starter like me this sounds good, and at times you just get lost while reading the docs lol, hope it helps some one.
Cheers
Try:
<form method="post" action="add_product/">
{% csrf_token %}
{{ form.as_p }}
</form>
in your template, instead of hand-coding the form's input tags. This shortcut will generate the form html for you, as well as print validation errors.
Make sure you return the form object to the template when:
There is no request.POST (form has not been submitted)
form.is_valid() fails (form has validation errors)
Of course, this is only to get you started. You really should read the docs

Categories

Resources