Make option in form disabled When already requested - django

I am building an web app that allows users to request items.
The user will select the item, the location, the start date and end date of the borrowing in a form.
I have the following model which I created the form from:
class Request(models.Model):
shootkit = models.ForeignKey(Shootkit, on_delete=models.CASCADE, null=True, blank=True)
username = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
request_start_date = models.DateField('Start Date')
request_end_date = models.DateField('End Date')
location = models.ForeignKey(Location, on_delete=models.CASCADE)
I have 5 Items in the database.
And here's the model:
class Shootkit(models.Model):
shootkit = models.CharField('Shootkit', max_length=100, null=True, blank=True)
And the def in views:
def request_form(request):
submitted = False
if request.method == "POST":
form = requestForm(request.POST)
if form.is_valid():
stock = form.save(commit=False)
stock.username = request.user
stock.save()
return HttpResponseRedirect('/request_form?submitted=True')
else:
form = requestForm(initial = {'username': request.user})
if 'submitted' in request.GET:
submitted = True
return render(request, 'request_form.html', {'form':form, 'submitted':submitted,})
The issue here is that I want to grey out (disable?) the items options which have been already requested.
so I know that I can filter the date greater or equal than today in objects in the Request table so it means that the item still unavailable. But I am not sure how to block/disable the item.
Can anyone help please?
(EDIT)
Apologies if my question is not clear.
Please see requestForm:
class requestForm(ModelForm):
class Meta:
model = Request
fields ='shootkit', 'location', 'request_start_date', 'request_end_date',
widgets = {
'request_start_date':DateInput(attrs={'class':'form-control'}),
'request_end_date':DateInput(attrs={'class':'form-control'}),
'shootkit': forms.Select(attrs={'class':'form-select'}),
#'username': forms.TextInput(attrs={'class':'form-control'}),
'location': forms.Select(attrs={'class':'form-select'})
}
and here is a pic of the request_form.html
my intention is that if any of those shootkits ( for example shootkit #2 and shootkit #4 have been requested and the end date of the borrowing is next week for example. Then the shootkits should appear as disabled in the form like this
I hope that makes more sense.
Thank you.

Related

django form not saving despite success message

I have been trying to teach myself something new this week by following various tutorials on Google APIs. I am hitting a wall on the form saving bit, so helping you can help.
Basically, I am trying to offer user the possibility to enter their first line of address for Google to then submit suggestions of addresses.
This first bit works (when I enter the first line of an address, I get generated a list of suggestions).
However, when I am trying to save the form, this doesn't seem to be saved anywhere. And I am not getting any error message either (if anything I am getting the a "success message" I am supposed to receive when the form is successfully submit).
I thought there might be a field that is not getting being populated in the html. So I tried to empty one and tried to save. But on this occasion I received an error message, saying the field is missing. Which makes me thing this hasnt anything to do with the fields in the form but probably how I save my form in the views.py.
I must admit I am way out of my comfort zone here (this ajax thing proved to be difficult to make work, and all I had to do was to follow a tutorial..), so I wouldnt be surprised I am not capturing all the code needed for you to help. If so let me know what you need to see.
Views.py
def is_ajax(request):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
def profile(request):
result = "Error"
message = "There was an error, please try again"
form = profileform(instance = request.user)
Profile.objects.get_or_create(user=request.user)
if is_ajax(request=request):
form = profileform(data = request.POST, instance = request.user)
if form.is_valid():
obj = form.save()
obj.has_profile = True
obj.save()
result = "Success"
message = "Your profile has been updated"
else:
message = FormErrors(form)
data = {'result': result, 'message': message}
return JsonResponse(data)
else:
context = {'form': form}
context['google_api_key'] = settings.GOOGLE_API_KEY
context['base_country'] = settings.BASE_COUNTRY
return render(request, 'main/profile.html', context)
Froms.py
from .models import Profile
class ProfileForm(forms.ModelForm):
address = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
town = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
county = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
post_code = forms.CharField(max_length=8, required=True, widget = forms.HiddenInput())
country = forms.CharField(max_length=40, required=True, widget = forms.HiddenInput())
longitude = forms.CharField(max_length=50, required=True, widget = forms.HiddenInput())
latitude = forms.CharField(max_length=50, required=True, widget = forms.HiddenInput())
class Meta:
model = Profile
fields = ['address','town','county','post_code','country','longitude','latitude']
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
address = models.CharField(verbose_name="Address",max_length=100, null=True, blank=True)
town = models.CharField(verbose_name="Town",max_length=100, null=True, blank=True)
county = models.CharField(verbose_name="County",max_length=100, null=True, blank=True)
post_code = models.CharField(verbose_name="Post Code",max_length=8, null=True, blank=True)
country = models.CharField(verbose_name="Country",max_length=100, null=True, blank=True)
longitude = models.CharField(verbose_name="Longitude",max_length=50, null=True, blank=True)
latitude = models.CharField(verbose_name="Latitude",max_length=50, null=True, blank=True)
captcha_score = models.FloatField(default = 0.0)
has_profile = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
def __str__(self):
return str(self.user)
Interesting that you're getting to your success message. Your issue might be that you're passing a user instance to a form that expects a profile model (not sure why this doesn't throw an error though):
# capture the profile that is created with get_or_create
profile, created = Profile.objects.get_or_create(user=request.user)
if is_ajax(request=request):
form = profileform(data = request.POST, instance=profile) # change instance
Instead of
else:
message = FormErrors(form)
Try this
else:
message = form.errors

Django ModelChoiceField Issue

I've got the following Situation, I have a rather large legacy model (which works nonetheless well) and need one of its fields as a distinct dropdown for one of my forms:
Legacy Table:
class SummaryView(models.Model):
...
Period = models.CharField(db_column='Period', max_length=10, blank=True, null=True)
...
def __str__(self):
return self.Period
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'MC_AUT_SummaryView'
Internal Model:
class BillCycle(models.Model):
...
Name = models.CharField(max_length=100, verbose_name='Name')
Period = models.CharField(max_length=10, null=True, blank=True)
Version = models.FloatField(verbose_name='Version', default=1.0)
Type = models.CharField(max_length=100, verbose_name='Type', choices=billcycle_type_choices)
Association = models.ForeignKey(BillCycleAssociation, on_delete=models.DO_NOTHING)
...
def __str__(self):
return self.Name
Since I don't want to connect them via a Foreign Key (as the SummaryView is not managed by Django) I tried a solution which I already used quite a few times. In my forms I create a ModelChoiceField which points to my Legacy Model:
class BillcycleModelForm(forms.ModelForm):
period_tmp = forms.ModelChoiceField(queryset=SummaryView.objects.values_list('Period', flat=True).distinct(),
required=False, label='Period')
....
class Meta:
model = BillCycle
fields = ['Name', 'Type', 'Association', 'period_tmp']
And in my view I try to over-write the Period Field from my internal Model with users form input:
def billcycle_create(request, template_name='XXX'):
form = BillcycleModelForm(request.POST or None)
data = request.POST.copy()
username = request.user
print("Data:")
print(data)
if form.is_valid():
initial_obj = form.save(commit=False)
initial_obj.ModifiedBy = username
initial_obj.Period = form.cleaned_data['period_tmp']
initial_obj.Status = 'Creating...'
print("initial object:")
print(initial_obj)
form.save()
....
So far so good:
Drop Down is rendered correctly
In my print Statement in the View ("data") I see that the desired infos are there:
'Type': ['Create/Delta'], 'Association': ['CP'], 'period_tmp': ['2019-12']
Still I get a Select a valid choice. That choice is not one of the available choices. Error in the forms. Any ideas??

Django: ManyToMany relationship and database duplicity

So I have models GeneralUser, Business, Brand, Bottle.
class Business(models.Model):
name = models.CharField(max_length=100, blank=False)
owner = models.ForeignKey(GeneralUser, on_delete=models.CASCADE, blank=False, related_name="businesses")
class Brand(models.Model):
name = models.CharField(max_length=150, blank=False, unique=True)
businesses = models.ManyToManyField(Business, related_name="brands", blank=False)
class Bottle(models.Model):
name = models.CharField(max_length=150, blank=False)
slug = models.SlugField(max_length=550, default="", null=False, blank=False)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=False, related_name="bottles")
Each Brand can belong to many businesses, and each Business can hold many brands.
My goal is to not have Brand objects created if they already exist in the database.
I've built a Bottle ModelForm which also allows the user to choose and create a new Brand in the process.
But the problem I'm facing now is this:
Users only see brands associated with the Business.
User 1 created Brand X → OK
User 2 tries to create Brand X → IntegrityError unique constraint failed.
Which I totally understand and not surprised by. My question is what's the best solution in this case?
The BottleCreateForm is pretty standard:
class BottleCreateForm(ModelForm):
class Meta:
model = Bottle
fields = ['name', 'brand' , 'vintage', 'capacity']
But I'm also adding an "Add New" HTML button, which calls the brand create view:
def add_brand(request):
response_data = {}
if request.method == "POST":
response_data['success'] = False
form = BrandCreateForm(request.POST)
if form.is_valid():
print("form valid!")
users_business = Business.objects.filter(owner=request.user).first()
form.save(commit=True)
users_business.brands.add(form.instance)
response_data['pk'] = form.instance.id
response_data['success'] = True
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
else:
response_data['errors'] = form.errors
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)

How to intercept and control saving a Django POST form?

When the user is required to fill his profile, he picks a city from the Google Places Autocomplete and posts the form, in the view I extract the city Id from the Google API based on the posted text (I use the same id as pk in my db) and try to extract a city from my db.
These are the models:
class City(models.Model):
#extracted from the Google API
city_id = models.CharField(primary_key=True, max_length=150)
name = models.CharField(max_length=128, blank=True)
country = models.CharField(max_length=128, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', primary_key=True)
city = models.ForeignKey(City, blank=True, null=True)
prof_pic = models.ImageField(blank=True, upload_to='profile_pictures')
This is the view:
def createprofile(request):
if request.method =='POST':
user = User.objects.get(username=request.user.username)
user_form = UserForm(data=request.POST, instance=user)
profile_form = UserProfileForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.save()
profile = profile_form.save(commit=False)
profile.user = user
#brings back the city search result as text
searched_city = request.POST['city']
#brings back city ID from the Google API
searched_city_id = population_script.get_city_json(searched_city.replace(" ", ""))['results'][0]['id']
#If it's a valid city
if searched_city_id != -1:
city = City.objects.get(city_id = searched_city_id)
profile.city = city#this is what I want to happen!
else:
return HttpResponse("There's no such city, please try a different query.")
if 'prof_pic' in request.FILES:#now save the profile pic
profile.prof_pic = request.FILES['prof_pic']
print("PROF PIC IS: " + profile.prof_pic.url)
else:
profile.prof_pic = 'images/anon.png'
profile.save()
if 'next' in request.GET:
return redirect(request.GET['next'])
else:
print (user_form.errors, profile_form.errors)
else:
user_form = UserForm()
profile_form = UserProfileForm()
return render(request,
'excurj/createprofile.html', {'user_form':user_form, 'profile_form':profile_form})
However, I keep receiving an error that what's been posted is just text while the city needs to be a City object. I can save the profile pic ok though.
Cannot assign "'Dubai - United Arab Emirates'": "UserProfile.city"
must be a "City" instance.
edit: these are the forms:
class UserForm(forms.ModelForm):
first_name = forms.CharField(
label = "First Name:",
max_length = 80,
required = True
)
last_name = forms.CharField(
label = "Last Name:",
max_length = 80,
required = True,
)
class Meta:
model = User
fields = ('first_name', 'last_name')
class UserProfileForm(forms.ModelForm):
city = forms.CharField(
label = "Your Current City:",
max_length = 200,
required = True,
)
class Meta:
model = UserProfile
fields = ('city','prof_pic', 'dob', 'sex', 'education', 'career', 'about_you',
'music_movies_books', )
Please provide a related_name to the city field in the UserProfile.
I worked around this by creating a new UserProfile field called city_search_text which saves the searched text thus it of course does not return any error. I then receive it in the POST request and comfortable pull the proper city in the view.
I handled a similar issue by overriding my forms' clean method. Something like the following will work:
def clean(self):
# fix city problem
if self.cleaned_data.get("city") is not None:
self.cleaned_data['city'] = City.objects.get(id=self.cleaned_data.get("city"))
return self.cleaned_data

How to use a submit a one-to-many form

I have a list of employees who work at a site. Each site is owned by a User (using Django's standard user model).
I want to create a form that adds an employee and automatically links them to a site dependent on who the authenticated user is:
models.py:
class Employee(models.Model):
site = models.ForeignKey(Site, null=True)
employee_name = models.CharField(default='name', max_length=128, blank=False, null=False)
class Site(models.Model):
user = models.ForeignKey(User)
site_name = models.CharField(max_length=128, blank=False, null=False)
views.py:
site_profile = Site.objects.get(user=request.user)
if request.method == "POST":
form = EmployeeAddForm( request.POST )
if form.is_valid():
obj = form.save(commit=False)
obj.site = site_profile
obj.save()
return redirect('dashboard_home')
form = EmployeeAddForm()
return render(request, "dashboard/employees.html", {'form': form })
forms.py:
class EmployeeAddForm(forms.ModelForm):
class Meta:
model = Employee
fields = ( 'employee_name')
This code will add the employee to the database, but in django admin, list_display = 'site' results in Site object not the actual site name. It does not appear that the employee is linked to the site.
If I use obj.site = site_profile.id (adding .id), I get the error Cannot assign "1": "Employee.site" must be a "Site" instance.
Found the error: the above code is correct, I simply had a tab ordering error in my Site modeL
class Site(models.Model):
...
def __str__(self):
return self.site_name
def should have been inserted 1 tab inwards.