Django - Getting specific data from model - django

I'm trying to make a booking system (for apartments). The idea is that users can pick a start date and an end date and book the apartment if it isnt alrdy booked.
I have a Reservation model with a "start_date" and a "end_date" that I use to determine the dates for the apartment booking.
Users the JQuery Ui Date Picker (a small calendar) to pick dates. The "unavailable" dates are grayed out.
This is how I did that :
<script type="text/javascript">
var unavailableDates = ['{{ reservation.all.0.start_date|date:"d-m-Y" }}', '{{ reservation.all.0.end_date|date:"d-m-Y" }}'];
console.log(unavailableDates);
function unavailable(date) {
dmy = date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear();
if ($.inArray(dmy, unavailableDates) == -1) {
return [true, ""];
} else {
return [false, "", "Unavailable"];
}
}
$(function () {
$("#datepicker").datepicker({
dateFormat: 'MM-dd-yy',
beforeShowDay: unavailable
});
});
</script>
I am using ...
{{ reservation.all.0.start_date|date:"d-m-Y" }}
...to gray out the date in the calendar.
My reservation model looks like this:
class Reservation(models.Model):
apartment = models.ForeignKey(Apartment, related_name='reservations',
on_delete=models.CASCADE, blank=True, null=True)
start_date = models.DateField(blank=True, null=True, unique=True)
end_date = models.DateField(blank=True, null=True, unique=True)
name = models.CharField(default="", max_length=200, unique=True)
def __str__(self):
return self.name
And in my view I have:
def apartment_view(request, apartment_id):
reservation = Reservation.objects.filter(apartment__pk=apartment_id)
apartment = get_object_or_404(Apartment, pk=apartment_id)
context = {'apartment': apartment, }
form = ReservationForm()
if request.method == 'GET':
form = ReservationForm()
elif request.method == 'POST':
form = ReservationForm(request.POST)
if form.is_valid():
reservation = form.save(commit=False)
reservation.apartment = apartment
reservation.save()
form.save()
return HttpResponseRedirect('/booking/')
args = {}
args['form'] = form
args['apartment'] = context
args['reservation'] = reservation
return render(request, 'booking/apartment.html', args)
Since I need my reservations to be filtered to the correct apartment.
My question is, how can I query for ALL dates related to an apartment, both start and end dates ?
So I need all dates for apartment 1, then all dates for apartment2...etc.
I'm not sure how to go on about this, I apologize if the post is confusing.
Thanks !!!

Later edit:
After looking at your JS code you might wanna do something like this in your view:
import itertools
unavailable_dates = apartment.reservations.values_list('start_date', 'end_date')
context['unavailable_dates'] = list(itertools.chain(*unavailable_dates)) # to convert _list_ of tuples to simple list
You can get start/end dates for reservations linked to an apartment like this:
apartment = get_object_or_404(Apartment, pk=apartment_id)
context['appartment_reservations_dates'] = apartment.reservations.values_list('start_date', 'end_date')
If you want to work with the entire Reservation object directly in your HTML template you can do it like this:
{% for reservation in apartment.reservation.all %}
{{ reservation.start_date }} - {{ reservation.end_date }}
{% endfor %}
Your view might look like this:
def apartment_view(request, apartment_id):
context = dict()
context['apartment'] = get_object_or_404(Apartment, pk=apartment_id)
context['reservations'] = apartment.reservations.all() # if you want all existing reservations for this specific appartment
form = ReservationForm()
if request.method == 'GET':
form = ReservationForm()
elif request.method == 'POST':
form = ReservationForm(request.POST)
if form.is_valid(): # you can add extra validation here making sure whoever tries to book is booking for an available time interval
reservation = form.save(commit=False)
reservation.apartment = apartment
reservation.save()
form.save()
return HttpResponseRedirect('/booking/')
context['form'] = form
return render(request, 'booking/apartment.html', args)

Related

Filtering data in real time in django forms

I have two problems strictly. The first is the problem with the field type in forms.py. Because I'm trying to use a foreign key as a value in the check box and I have the error that the "int () must be a string, a byte-like object or a number, not" ModelChoiceField "and I don't know what to do with it. The second main problem is data filtering in the interface in real time. What I mean? I have a user model like:
# user/models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
country= models.ForeignKey(Country, on_delete=models.SET_NULL, null=True)
city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True)
year = models.IntegerField(choices=YEARS, default=1)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
And in forms I want to see only those cities that are in the selected country. For example, we have record like:
London, United Kingdom;
York, United Kingdom;
Berlin, Germany;
and if the user chooses Germany, he should only see Berlin in the area with the cities.
I hope you know what I want to achieve and someone will be able to help me.
# forms.py:
class ProfileUpdateForm(forms.ModelForm):
country =forms.ModelChoiceField(queryset=Country.objects.all())
city = forms.ModelChoiceField(queryset=City.objects.filter(country=country))
class Meta:
model = Profile
fields = ['website','country', 'city', 'year', 'image']
# city/models.py
class Country(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class City(models.Model):
name = models.CharField(max_length=100)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
code = models.CharField(max_length=7)
def __str__(self):
return self.name
EDIT:
I think you need this piece of code:
#login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST,
request.FILES,
instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, 'Twoje dane zostały uaktualnione!')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form,
}
return render(request, 'users/profile.html', context)
It's easy, don't worry. But you should use basic Ajax request for this so you'll learn something new, be happy!
Firstly, when If user doesn't select any country yet, we should hide all cities on form, or when user select undefined country (maybe you don't have city information in your country table) we should handle all errors. So add this lines to your form model:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['city'].queryset = City.objects.none()
if 'country' in self.data:
try:
country_id = int(self.data.get('country'))
self.fields['city'].queryset = City.objects.filter(country_id=country_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty City queryset
elif self.instance.pk:
self.fields['city'].queryset = self.instance.country.city_set.order_by('name')
And add new function for get filtered cities when user select a country to your view before main view.
It looks like that:
def load_cities(request):
country_id = request.GET.get('country')
cities = City.objects.filter(country_id=country_id).order_by('name')
return render(request, 'city_dropdown_list_options.html', {'cities': cities})
This little function will request to 'country' field in your form and send filteder cities.
Let's create new html file in your directory like 'city_dropdown_list_options.html':
<option value="">---------</option>
{% for city in cities %}
<option value="{{ city.pk }}">{{ city.name }}</option>
{% endfor %}
Add new url to your urls.py:
path('ajax/load-cities/', views.load_cities, name='ajax_load_cities')
Now it’s time to create an AJAX request in your form. In the example below I’m using jQuery, but you can use any JavaScript framework (or just plain JavaScript) to create the asynchronous request:
{% block content %}
<h2>Your Form</h2>
<form method="post" id="ProfileUpdateForm" data-cities-url="{% url 'ajax_load_cities' %}" novalidate>
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button type="submit">Save</button>
Go!
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_country").change(function () {
var url = $("#ProfileUpdateForm").attr("data-cities-url"); // get the url of the `load_cities` view
var countryId = $(this).val(); // get the selected country ID from the HTML input
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/ajax/load-cities/)
data: {
'country': countryId // add the country id to the GET parameters
},
success: function (data) { // `data` is the return of the `load_cities` view function
$("#id_city").html(data); // replace the contents of the city input with the data that came from the server
}
});
});
</script>
{% endblock %}
Ta da! If you select a country, you will see just cities that filtered for country.
Is it magic, right? I learned all of these from https://www.simpleisbetterthancomplex.com, you can search with this title: 'How to Implement Dependent/Chained Dropdown List with Django?'. Good luck bro!
Yep def load_cities function must be created in views.py, just above your main function.
Your forms.py should look like below and you can delete country and city definitions because you already defined your fields in class Meta:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['website','country', 'city', 'year', 'image']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['city'].queryset = City.objects.none()
if 'country' in self.data:
try:
country_id = int(self.data.get('country'))
self.fields['city'].queryset = City.objects.filter(country_id=country_id).order_by('name')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['city'].queryset = self.instance.country.city_set.order_by('name')
Could you show your main function that is renderer your form (in views.py)?

Saving values set in Frontend to Django Database

I have a website which allows Users to pick a few hobbies they enjoy. Currently the website loads these hobbies from a Model and lists them with a Checkbox. What I want to do is when a User saves the form, it should also save these Checkbox values to the database - i.e if they tick Football, the database should save the fact that this User enjoys football. I am new to Django and Python so not too sure how to accomplish this. Here is the code I am using. This is the Models.py file for the Hobbies:
TYPES = (
("Football", "Football"),
("Cricket", "Cricket"),
("Swimming", "Swimming"),
("Cycling", "Cycling")
)
class Hobby(models.Model):
myfield = models.CharField(max_length=50, choices = TYPES, default=TYPES[0], null=True)
football = models.BooleanField(default = False)
cricket = models.BooleanField(default = False)
swimming = models.BooleanField(default = False)
cycling = models.BooleanField(default = False)
This is the relevant views.py file:
def profile(request, user):
# use this for debugging:
# import pdb; pdb.set_trace()
if 'email' in request.POST:
email = request.POST['email']
gender = request.POST['gender']
dob = request.POST['dob']
## hobby = request.POST['hobby']
if user.profile:
user.profile.email = email
user.profile.gender = gender
user.profile.dob = dob
## user.profile.hobby = hobby
user.profile.save()
else:
profile = Profile(email=email, gender=gender, dob=dob)
profile.save()
user.profile = profile
user.save()
context = {
'appname': appname,
'username': user.username,
'profile' : user.profile,
'all_hobbies': [hobby[0] for hobby in TYPES],
'loggedin': True
}
return render(request, 'mainapp/profile.html', context)
And lastly the HTML/JS code to display the information:
<span class="fieldname">Hobbies</span>
{% for hobby in all_hobbies %}
<input type="checkbox" name={{hobby}} value={{hobby}}> {{hobby}}<br>
{% endfor %}
<input type='submit' value='Save'>
What I want is a way to check whether the checkboxes have been ticked, and if so to change the value of the BooleanField in the database/model to either True or False. However, I am not sure where to do this, in the views or the JS code. Can someone help me out here? Many thanks.
For this case to list all hobbies and update them again. You need to use a Form Class. Example. I just want to mention a single thing, the code below its just a guideline of what you should do. Because Its kind of confusing the logic you want to approach. Since you are not handleing a user as a session but just a plain key.
model.py
my_choices = (
(0, "None"),
(1, "Football"),
(2, "Cricket"),
(3, "Swimming"),
(4, "Cycling"),
)
class Hobby(models.Model):
user = models.ForeignKey(Profile, on_delete=models.DO_NOTHING)
field = models.integerField(choices=my_choices, default=0)
forms.py
class HobbyForm(ModelForm):
class Meta:
model = Hobby
fields = ["field"]
views.py
def profile(request,user):
try:
profile_object = Profile.get(id=user)
if request.method.POST:
form = HobbyForm(request.POST)
if form.is_valid():
profile_object.field = form.cleaned_data["field"]
profile_object.save()
context = {
"form": form,
"profile": profile_object,
}
return render(request, 'mainapp/profile.html', context)
else:
context = {
"form": form,
"profile": profile_object,
}
return render(request, 'mainapp/profile.html', context)
else:
context = {
"form": form,
"profile": profile_object,
}
return render(request, 'mainapp/profile.html', context)
except Profile.DoesNotExists:
context = {
"form": form,
"profile": profile_object,
}
return render(request, 'mainapp/profile.html', context)
profile.html
<form action="myurl/{{profile.id}}/" method="post">
{% csrf_token %}
{% form.as_p %}
<input type="submit" value="OK">
</form>

Django ForeignKey Model Form

I'm newbie on Django. I have two model and one of this model have Foreign Key. I'm using Model Form in forms and when I fill the form my foreign key field return null. What I want is when I fill the form foreign key field, fill according to the pointed out by the foreign key.
Models:
class customerInfo(models.Model):
customerName = models.CharField(max_length = 50)
customerContent = models.TextField(max_length = 50)
createdDate= models.DateTimeField(auto_now_add = True)
def __str__(self):
return self.customerName
class productInfo(models.Model):
username = models.CharField(max_length = 50)
passwd = models.CharField(max_length = 50)
destIp = models.CharField(max_length = 50)
hostname = models.CharField(max_length = 50)
productName = models.CharField(max_length = 50)
customer = models.ForeignKey(customerInfo,on_delete = models.CASCADE,null=True)
def __str__(self):
return self.productName
Forms:
class customerForm(forms.ModelForm):
class Meta:
model = customerInfo
fields = (
"customerName",
)
class addProductForm(forms.ModelForm):
class Meta:
model = productInfo
fields = (
"productName",
)
class productInfoForm(forms.ModelForm):
class Meta:
model = productInfo
fields = (
"username",
"passwd",
"destIp",
"hostname",
)
Views:
#login_required(login_url = "/")
def addCustomer(request):
form = customerForm(request.POST or None)
content = {"form" : form,}
if form.is_valid():
form.save()
customerName = form.cleaned_data['customerName']
return redirect("addproduct")
else:
return render(request,"addcustomer.html",content)
#login_required(login_url = "/")
def addProduct(request):
form = addProductForm(request.POST or None)
content = {"form" : form}
if form.is_valid():
global productName
productName = form.cleaned_data['productName']
return redirect("addproductinfo")
return render(request,"addproduct.html",content)
#login_required(login_url = "/")
def addProductInfo(request):
form = productInfoForm(request.POST or None)
content = {"form" : form}
if form.is_valid():
p = form.save(commit = False)
p.productName = productName
p.save()
return redirect("customer")
return render(request,"addproductinfo.html",content)
As a result, I want to see the customer's products when I click on the customer name. Not all products.
Before I can do that, the customer id fields needs to be full.
I hope you understood me.
Your question and code sample is not clear.
First of all you should break down your model into several use cases:
Customer: list of customers, Create, Read, Update & Delete (CRUD) customer
Product: list of products, Create, Read, Update & Delete (CRUD) product
From the list of customers you can Read one and on the 'detail view displayed' you can Create, Update or Delete it.
From the list of products you can Read one and on the 'detail view displayed' you can Create, Update or Delete it.
Passing from the list of customer to the list of product can be done via an extra Button/Link displayed per line on your Customer List, so as your Button/Link used to display any Customer Detail.
The customer PrimaryKey (PK) is passed to the detail via the url definition.
path('customer/<pk>', views.customer_detail_view, name='customer_detail'),
This url is only for display. You're also need one for each DB operation: Create, Update, Delete. Find below urls.py code example for your customer. You'll need the same for the products.
from django.urls import path
from . import views
urlpatterns = urlpatterns + [
path('customer', views.customer_list_view, name='customer_list'),
path('customer/add', views.customer_add_view, name='customer_add'),
path('customer/<pk>', views.customer_detail_view, name='customer_detail'),
path('customer/<pk>/upd', views.customer_update_view, name='customer_update'),
path('customer/<pk>/del', views.customer_delete_view, name='customer_delete'),
]
Note that create doesn't pass 'pk' since it is unknown yet...
The call to the Detail View from the List View is done in your html template
<tbody>
{% for i in customer_list %}
<tr>
<td>{{ i.customerName }}</td>
<td>{{ i.customerContent|default_if_none:"" }}</td>
</tr>
{% endfor %}
</tbody>
The argument is passed by kwargs (dict) via the url and if you use ClassBasedView (generic.DetailView) it will be handled automatically. If not, you have to grab the kwargs like: kwargs.get('pk') or kwargs.pop('pk') the last one remove 'pk' from the kwargs. You could also pass the 'pk' using args (no pk key assignement) {% url 'customer_detail' i.id %}. This can also be defined directly in a get_absolute_url function of your model.
def get_absolute_url(self):
return reverse_lazy('customer_detail', args=[str(self.id)])
or
def get_absolute_url(self):
return reverse_lazy('customer_detail', kwargs={'pk': self.pk})
By doing that way you'll also be able to manage your 'productName' global variable, which should be avoided! By the way I don't understand why you're willing to separate the creation of productName and productInfo??? Why not keeping them all together?
Finally, if you want to display several possible encoding line for your Product, you should take a look at Django-FormSet. Search google for FormSet Tutorial but this is more an advanced feature.
A ProductFormset with 5 possible encoding lines would look like:
from django.forms import modelformset_factory
ProductFormset = modelformset_factory(
productInfo,
fields=('productName', ),
extra=5,
widgets={'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter product Name here'
})
}
)
If you want to reuse the productInfo model then you shoud you models.ManyToManyField instead of the ForeignKey. As i understand correctly you want to have a product that multiple of the customers can "connect" to , right ?
for more --> https://docs.djangoproject.com/en/2.1/ref/models/fields/
and more --> https://www.revsys.com/tidbits/tips-using-djangos-manytomanyfield/
My usage:
class EventVocab(models.Model):
word = models.CharField(max_length = 30)
word_chinese = models.CharField(max_length = 30,blank=True, null=True)
explanation = models.TextField(max_length = 200)
example = models.TextField(max_length = 100)
word_audio = models.FileField(blank=True, null=True)
explanation_audio = models.FileField(blank=True, null=True)
example_audio = models.FileField(blank=True, null=True)
class UserVocab(models.Model):
event_vocab = models.ManyToManyField(EventVocab, related_name='event_vocab')
current_user = models.ForeignKey(User, related_name="vocab_owner", on_delete=models.CASCADE)
In this example UserVocab (in your case product) can be connected to just one User, but one user can have multiple event_vocabs (products)

Django to give limitation for postjob

Am having tables in database like EmployerRegistration and PostJob. Am relating those tables with empid(foreign key of PostJob). Using this empid I need to PostJob at maximum of 5.I want to display alert message like number of post available while login. Some one help how to give views for this condition.
models.py
class EmployerRegistration(models.Model):
username=models.CharField(max_length=30)
password=models.CharField(max_length=30)
class PostJob(models.Model):
emp = models.ForeignKey(EmployerRegistration)
jobtitle = models.CharField(max_length=30)
jobsummary = models.TextField()
key_skills = models.CharField(max_length=30)
experince = models.IntegerField(default=0)
salary = models.IntegerField(default=0)
views.py
def your_view(request, empid):
msg=""
if request.method == "POST":
jobs = PostJob.objects.filter(emp_id=empid).count()
if jobs <= 5:
//save
else:
msg = Your not allow to add new post job.
return render(request, 'page.html', {'msg': msg})
template
<script>
$(document).ready(function(){
var msg = {{msg}}
if(msg != ""){
alert(msg);
}
});
</script>

django: data not save in db when using two models and two form in one template

I have difficulty saving my form into db. Every time I click on save it didn't save in db/admin. Can't seem to figure it out. I hope somebody out there can help me with this.
This project is about collecting information on a new customer for a construction company and if the customer exist in the db then it will view the detail of the particular customer and be able to edit the particular customer when needed and be able to overwrite the data when save. This information gather from a customer model and building model and customerForm and buildingForm and display in customer template.
models.py
class customer(models.Model)
F_NAME = models.CharField(max_length = 50)
L_NAME = models.CharField(max_length = 50)
ADD = models.CharField(max_length = 60, blank =True)
EMAIL = models.EmailField()
def __unicode__(self):
return '%s' % ( self.F_NAME )
class building(models.Model):
CUSTOMER = models.ForeignKey(customer)
B_USE = models.CharField(max_length = 2, blank = True, choices = c.Use)
B_FLOORSPACE = models.IntegerField(default=0)
B_YEAR = models.IntegerField(null = True, blank = True)
B_TYPE = models.CharField(max_length = 2, blank = True, choices = c.Type)
def __unicode__(self):
return '%s' % (self.B_USE)
forms.py
class customerForm(ModelForm):
F_NAME = forms.CharField(widget=forms.TextInput(attrs={'size':'34'}))
L_NAME = forms.CharField(widget=forms.TextInput(attrs={'size':'34'}))
EMAIL = forms.CharField(widget=forms.TextInput(attrs={'size':'19'}))
ADD = forms.CharField(widget=forms.TextInput(attrs={'size':'34'}))
class Meta:
model = customer
class buildingForm(ModelForm):
CUSTOMER = forms.CharField(widget=forms.TextInput(attrs={'size':'20'}))
B_FLOORSPACE = forms.CharField(widget=forms.TextInput(attrs={'size':'4'}))
B_YEAR = forms.CharField(widget=forms.TextInput(attrs={'size':'4'}))
class Meta:
model = building
exclude = ('CUSTOMER',)
widgets = {'B_USE' : RadioSelectNotNull(),
'B_TYPE' : RadioSelectNotNull(),
}
views.py
def customerView(request ,**kwargs ):
context ={}
try:
this_customer = customer.objects.get(id = kwargs ['pk'])
except:
this_customer = customer.objects.create(id = kwargs['pk'])
try:
bform = buildingForm(instance=building())
context['build']=True
context['bform']=bform
except:
context['build']=False
form = customerForm(instance=this_customer)
context['form']= form
if request.method == 'POST':
form = customerForm(request.POST, instance = customer())
bform = [buildingForm(request.POST, prefix = str(x), instance = building()) for x in range (0,3)]
if form.is_valid() and all ([bf.is_valid() for bf in bform]):
new_customer = form.save()
for bf in bform:
new_build = bf.save(commit = False)
new_build.CUSTOMER = new_customer
new_build.save()
return HttpResponseRedirect('customer.html')
else:
form = customerForm(instance=customer())
bform = [buildingForm(prefix=str(x), instance=building())for x in range(0,3)]
return render_to_response('customer.html',{'customer_form':form, 'build_form':bform}, context_instance = RequestContext(request))
customer.html
<form action="" method="post">
<button type="submit" name="customer">Save</button>
{% csrf_token %}
{{ form.id }}
...more code...
<table>
<tr><td><div>First Name</div>{{ form.F_NAME }}</td></tr>
<tr><td><div>Last Name</div>{{ form.L_NAME }}</td></tr>
</table>
....more code....
{% if build %}
...more code....
<table>
<tr><td><div>Build Use</div></td><td>{{ bform.B_USE }}</td>
<td><div>Build Space</div></td><td>{{ bform.B_FLOORSPACE }}</td>
</tr>
...more code...
</form>
I have try this demo on multiple model entry in single form
http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/
and
Django: multiple models in one template using forms
and
How to use two different Django Form at the same template?
but can't see where the mistake is. My task is quite simple for now, I just have to save all the information that has been keyed-in and saved in the database.
You probably have a validation error somewhere. Since you're not displaying any of the form errors in the template, your form will just be redisplayed with no explanation.