Dynamic DateTime field in Django form - django

I'm working on an Django 3 app which allows users to add and remove data requests for review. The user will select the user and site from a list and depending on the availability of data will populate a date/time field to be able to select a start and end date range. I'm using the Tempus Dominus time date picker to set the available dates in the date picker and am hardcoding them for the time being. The end goal will be to query Elasticsearch for them but hardcoded is fine for now.
My question is; At the moment the form is static, meaning none of the selections change the content in other sections of the form.
How can i make it dynamic so that the list of enableDates dates changes when the user selects a different user or site from the dropdown lists? Just to be clear, the list of users and sites will never change, it's just the enableDates that need to change.
I'd appreciate any help or advice.
At the moment the form for submitting a request, along with displaying any existing requests looks like:
This is my MODEL for storing data requests:
class DataRequest(models.Model):
USERS = [
('User1', 'User1'),
('User2', 'User2'),
('User3', 'User3'),
]
users = models.CharField(
choices=USERS,
max_length=32,
default='User1',
)
SITE = [
('Site1', 'Site1'),
('Site2', 'Site2'),
('Site3', 'Site3'),
]
site =models.CharField(
choices=SITE,
max_length=32,
default='Site1',
)
start = models.DateTimeField()
end = models.DateTimeField()
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
def get_request(self):
request = {
"id": self.id,
"users": self.users,
"site": self.site,
"start": self.start,
"end": self.end,
}
return request
This is the FORM:
class RequestForm(ModelForm):
start = forms.DateTimeField(
widget=DateTimePicker(
options={
},
attrs={
'append': 'fa fa-calendar',
'input_toggle': True,
'icon_toggle': True,
}
)
)
end = forms.DateTimeField(
widget=DateTimePicker(
options={
'useCurrent': False,
'enabledDates':["2021-02-20",], // hard coded dates go here
},
attrs={
'append': 'fa fa-calendar',
'input_toggle': True,
'icon_toggle': True,
}
)
)
class Meta:
model = DataRequest
fields = ['users','site', 'start','end']
def __init__(self, *args, **kwargs):
super(RequestForm, self).__init__(*args, **kwargs)
self.fields['users'].widget.attrs.update({'class' : 'form-control'})
self.fields['site'].widget.attrs.update({'class' : 'form-control'})
this is the VIEW for adding and removing requests, the list of existing data requests is sent over in the page context:
def delRequest(request, pk):
if request.method == 'POST':
request = DataRequest.objects.get(id=pk)
request.delete()
return redirect('dashboard')
def addRequest (request):
if request.method == 'POST':
form = RequestForm(request.POST)
if form.is_valid():
DataRequest.objects.create(
user=request.user,
users=form.cleaned_data['users'],
site=form.cleaned_data['site'],
start=form.cleaned_data['start'],
end=form.cleaned_data['end']
)
print("Added new Request")
return redirect('dashboard')

Related

Django foreign key as options for autocomplete, error: Select a valid choice

Thanks in advance for your help. I am building my first Django project, a ticketing application that will have over 100 items to choose from.
I'm trying to display a list of Items(foreign key objects) for the user to choose from when creating a ticket in an MDBootstrap autocomplete field. The items have both a name and ID and I want both to appear in the autocomplete options when the user is searching.
My form works when I display only the 'item_id' from the Item model in the autocomplete data (found in the script tags at the bottom of the template). But when I display the string with both 'item_name" and 'item_id'(as you see below), my form won't submit and I get the error "Select a valid choice. That choice is not one of the available choices."
How can I display both 'item_name' and 'item_id' from the Item model in the autocomplete options but then have my form submit properly recognizing the 'item_id' for its 'item' foreign key field?
Edited to add Example: Item model has object with item_name 'Large Tractor 7', item_id 'LT7'. I want the autocomplete to populate with 'Large Tractor 7 | ID: LT7'. I can make my form work if I only display 'LT7' because the Ticket model is looking for the item foreign key(i.e. LT7), but if I create the string with more information, the form produces the "Select a valid choice" error and I can't create the ticket.
models (each of these models is in a different app within the django project)
class Item(models.Model):
item_name = models.CharField(max_length=200)
item_id = models.CharField(max_length=200, primary_key=True)
class Ticket(models.Model):
ticket_id = models.AutoField(primary_key=True, null=False, blank=False)
item = models.ForeignKey(Item, on_delete=models.PROTECT, null=False, blank=False, related_name="tickets")
view
def TicketSubmit(request):
items = Item.objects.all()
if request.method == "POST":
item = request.POST.get("item")
form = TicketSubmitForm(request.POST)
photoform = TicketImageForm(request.POST or None)
files = request.FILES.getlist("ticket_photo")
if form.is_valid():
f = form.save(commit=False)
f.item = item
for i in files:
TicketPhoto.objects.create(ticket=f, ticket_photo=i)
messages.success(request, "Success! New Ticket Created")
return HttpResponseRedirect(reverse_lazy("home"))
else:
print(form.errors)
else:
form = TicketSubmitForm()
photoform = TicketImageForm()
context = {"form": form, "photoform": photoform, "items": list(items)}
return render(request, "home.html", context)
form
class TicketSubmitForm(forms.ModelForm):
class Meta:
model = Ticket
fields = ["item", "op_name", "ticket_text"]
widgets = {
"created_at": forms.HiddenInput,
"completed": forms.HiddenInput,
"item": forms.TextInput(
attrs={
"class": "form-control",
"id": "form11",
}
),
"op_name": forms.TextInput(
attrs={
"class": "form-control",
"id": "op_nameInput",
"placeholder": "Enter your name here",
}
),
"ticket_text": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Write a few words about the issue here",
"rows": "4",
}
),
}
main.js
const basicAutocomplete = document.querySelector('#basic');
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
new mdb.Autocomplete(basicAutocomplete, {
filter: dataFilter
});
template
<form id="ticket_form" role="form" action="" method="post" enctype="multipart/form-data" class="">{% csrf_token %}
<div id="basic" class="form-outline mx-5 mb-2">
{{form.item}}
<label class="form-label" for="form1">Find Item</label>
</div>
#other form fields and submit button
</form>
<script>
const data = [{% for i in items %}'{{ i.item_name }} | ID: {{ i.item_id }}', {% endfor %}];
</script>
The error it's that you're creating a different string to parse the data back when you're retriving the data on the form, that the one that Django are expecting, which is the unique identifier, or your primary key.
This is more a design problem, but you have a few option available.
One could be simply, before parse the data back, when an item is selected, like for example, 'LT7 - Large Tractor 7', have a Regex pattern to adecuate the input to what Django needs to work with your model, or even easier, split the str by -. and take the first element, that w'd be the primary key that you need.
pk: str = input_from_form_string.split(' - ')[0] # Your LT7 will be in this variable
Is a simpler way to acomplish your objective.
I was able to fix this in the following way.
if request.method == "POST":
updated_request = request.POST.copy()
item = request.POST.get("item")
item = item.split("ID:")[1].strip()
updated_request.update({"item": item})
form = TicketSubmitForm(updated_request)
photoform = TicketImageForm(request.POST or None)
files = request.FILES.getlist("ticket_photo")
if form.is_valid():
f = form.save()
for i in files:
TicketPhoto.objects.create(ticket=f, ticket_photo=i)
messages.success(request, "Success! New Ticket Created")
return HttpResponseRedirect(reverse_lazy("home"))
else:
print(form.errors)
else:
form = TicketSubmitForm()
photoform = TicketImageForm()
context = {"form": form, "photoform": photoform, "items": list(items)}
return render(request, "home.html", context)
Thanks #AlexVergara for getting me pointed in the right direction.

How to check if the fields already exist.?

I need to check if the fields already exist. If that field doesn't exist, then I need to create a new one. Like the availability check on a booking system
My Model
# Choices
time = [
('1', 'Morning'),
('2', 'Afternoon'),
('3', 'Evening'),
('4', 'Night'),
]
# Model
class Calender(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL,on_delete=models.SET_NULL)
date = models.DateField()
time = models.CharField(max_length=10, choices=time)
location = models.CharField(max_length=32)
My View
class CheckAvailabilityAPIView(APIView):
def post(self, request):
date = request.data.get('date')
time = request.data.get('time')
location = request.data.get('location')
calender = Calender.objects.all()
for obj in calender:
if obj.booked_date == date and obj.time == time and obj.location == location:
return Response({'response': 'Already exist'})
else:
user_id = request.user.id
user = User.objects.get(id=user_id)
serializer = VendorsCalenderSerializer(data=request.data)
if serializer.is_valid():
serializer.save(
user=user,
date=date,
time=time,
location=location
)
return Response({'response': 'Success', 'result': serializer.data})
else:
return Response({'response': 'Failed', 'error': serializer.errors},
status=status.HTTP_400_BAD_REQUEST)
Can someone help me with this.?
You can try using get_or_create()
Something like this :
appointment, created = Calender.objects.get_or_create(
user=user,
date=date,
time=time,
location=location
)
Returns a tuple of (object, created), where object is the retrieved or created object and created is a boolean specifying whether a new object was created.
Then based on created you can return customised response.
Reference : https://docs.djangoproject.com/en/2.2/ref/models/querysets/#get-or-create
And maybe their is a typo in Calender. Maybe you meant Calendar.
change calender = Calender.objects.all()
to
calender, result = Calender.objects.get_or_create(booked_date= date, section_timing = time, location = location)
if result is True, then new calender is created
You can also use filter :
calender = session.query(Calender).filter_by(**kwargs).first()
if calender:
return Response({'response': 'Already exist'})
else:

request.user is not letting me make changes to the current user?

In my view function, I'm trying to make 2 modifications to the current user; consider him a premium subscriber by marking model field is_premium_subscriber as True and adding him to a group named Premium Agents.
However the changes don't seem to be registering in my views.py! Here is my code:
def payment_response(request):
new_charge = PremiumSubscriptionCharge()
if request.method == "POST":
... Some code here
try:
... lots of code here
new_charge.agent = request.user # This line is working fine, meaning request.user is properly assigned to the current user
request.user.is_premium_subscriber = True # This is not working, is_premium_subscriber is still false after the request
premium_agent_group = Group.objects.get(name='Premium Agents')
premium_agent_group.user_set.add(request.user) # This is not working either, the current user does not get added to the group.
request.user.save() # I don't know if this is necessary, but I put it here just in case.
except stripe.error.CardError as ce:
... some code
else:
... some code
My user model for reference. I created a custom User model by inheriting AbstractUser... could this have caused the issue?
class Agent(AbstractUser):
is_premium_subscriber = models.BooleanField(default=False)
Full views function:
def payment_response(request):
new_charge = PremiumSubscriptionCharge()
if request.method == "POST":
token = request.POST.get("stripeToken")
try:
customer = stripe.Customer.create(
email = request.user.email,
source = token,
)
charge = stripe.Charge.create(
amount = 1500,
currency = 'cad',
customer = customer.id,
description = "Agent Premium Subscription"
)
subscription = stripe.Subscription.create(
customer=customer.id,
items=[
{
"plan": "premiumagent",
},
],
)
new_charge.stripe_charge_id = charge.id
new_charge.agent = request.user
new_charge.customer = charge.customer
new_charge.stripe_subscription_id = subscription.id
request.user.is_premium_subscriber = True
premium_agent_group = Group.objects.get(name='Premium Agents')
premium_agent_group.user_set.add(request.user)
request.user.save()
except stripe.error.CardError as ce:
return False, ce
else:
new_charge.save()
return redirect("payment_success")
Okay I was working on something else, came back to it, and now it is working. What the. Thank you to those who commented.

Django form data not submitting/visable in mysql database

I designed and built a Django 1.6.2 survey application using a SessionWizardView which is connected to a MySQL database.
The problem is that (as far as I can see) the submitted form data is not getting saved to the database. This is my first time building an application like this or even working with a database.
Could someone take a look at what I have done and my code and point out any mistakes I have made as to why I cannot see any content submitted by my survey form?
The last time I posted a similar question I was told I needed to create a Model for the data and it was suggested I use a ModelForm to create the table and columns in the database. I have done this but I am still not seeing my submitted content
My Process
I created the database in MySQL (Ver 14.14 Distrib 5.6.20) via
Terminal CREATE database django_db; and the tables in it are created
when I run the command python manage.py syncdb.
I can complete my survey both on my local machine and on the public server. No errors and it appears everything works fine
I have setup phpMyAdmin and can see the django_db database and survey_person model. However I can not seem to find any of the data that should be submitted by the form.
I have tried to use the search facility in phpMyAdmin to find any of the form data I submitted but cannot see it
I have exported the database as a .CSV file but it is empty.
If I use the insert facility in phpMyAdmin the data gets saved in the DB and I can return it when I use the search facilities. The same data is also in the CSV file when I export it.
This seem to suggest that I am missing a step somewhere in my application when it comes to submitting content to the DB.
Can anyone tell me where I am going wrong?
My Code
I have tried to limit the below to only relevant code
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_db',
'USER': 'root',
'PASSWORD': 'xxxxxxxxxxxxxxxxx',
'HOST': '127.0.0.1',
#'PORT': '',
}
}
urls.py
url(r'^surveyone/$', SurveyWizardOne.as_view([
SurveyFormA,
SurveyFormB,
SurveyFormC,
....
....
SurveyFormG,
SurveyFormH,
SurveyFormI
])),
forms.py
class SurveyFormA(forms.ModelForm):
birthdate = forms.DateField(widget=extras.SelectDateWidget(years = range(1995, 1900, -1)), required=False)
class Meta:
model = Person
fields = ['sender', 'birthdate', 'sex', 'relationship', 'state']
class SurveyFormB(forms.ModelForm):
class Meta:
model = Person
fields = ['internet_usage', 'smart_phone_ownership', 'smart_phone_usage']
widgets = {'internet_usage' : RadioSelectNotNull,
'smart_phone_ownership' : RadioSelectNotNull,
'smart_phone_usage' : RadioSelectNotNull,
}
class SurveyFormC(forms.ModelForm):
class Meta:
model = Person
fields = ['education', 'wages', 'presentage_savings', 'occupation', 'living']
widgets = {'education' : forms.RadioSelect,
'wages' : forms.RadioSelect,
'presentage_savings' : forms.RadioSelect,
'occupation' : forms.RadioSelect,
'living' : forms.RadioSelect,}
....
....
models.py
sender = models.EmailField(null=True, blank=True, verbose_name='What is your email address?')
birthdate = models.DateField(null=True, blank=True) #overwritten in forms.py so passed no more arguments
SEX = (
('MALE', 'Male'),
('FEMALE', 'Female'))
sex = models.CharField(null=True, blank=True, max_length=100, choices=SEX, verbose_name='What sex are you?')
RELATIONSHIP = (
('SINGLE', "Single"),
('INARELATIONSHIP', "In a relationship"),
('MARRIED', "Married"),
('DIVORCED', "Divorced"),
('SEPARATED', "Separated"),
('WIDOWED', "Widowed"),)
relationship = models.CharField(null=True, blank=True, max_length=100, choices=RELATIONSHIP, verbose_name='What is your relationship status?')
....
....
def __unicode__(self):
return self
views.py
My views.py are the most complex part of my application. Not sure if it necessary to show any but I thought just in case
class SurveyWizardOne(SessionWizardView):
def get_context_data(self, form, **kwargs):
context = super(SurveyWizardOne, self).get_context_data(form, **kwargs)
step = int(self.steps.current)
if step == 0:
self.request.session['path_one_images'] = ['P1D1.jpg', 'P2D2.jpg', 'P3D3.jpg', 'P4D4.jpg', 'P5D5.jpg', 'P6D6.jpg', 'P7D7.jpg', 'P8D8.jpg', 'P9D9.jpg']
self.request.session['instruction_task_one_images'] = ['IT1A.jpg', 'IT1B.jpg', 'IT1C.jpg']
self.request.session['instruction_task_two_images'] = ['IT2A.jpg', 'IT2B.jpg', 'IT2C.jpg']
self.request.session['images'] = []
self.request.session['slider_DV_values'] = []
PATH_ONE_IMAGES = self.request.session.get('path_one_images', [])
images = self.request.session.get('images', [])
slider_DV_values = self.request.session.get('slider_DV_values', [])
INSTRUCTION_TASK_ONE_IMAGES = self.request.session.get('instruction_task_one_images', [])
INSTRUCTION_TASK_TWO_IMAGES = self.request.session.get('instruction_task_two_images', [])
if step in range (0, 27):
self.request.session['path_one_images'] = PATH_ONE_IMAGES
self.request.session['images'] = images
self.request.session['slider_DV_values'] = slider_DV_values
self.request.session['instruction_task_one_images'] = INSTRUCTION_TASK_ONE_IMAGES
self.request.session['instruction_task_two_images'] = INSTRUCTION_TASK_TWO_IMAGES
if step == 0:
instruction_task_first_image = random.choice(INSTRUCTION_TASK_ONE_IMAGES)
context['display_image'] = instruction_task_first_image
elif step == 1:
instruction_task_second_image = random.choice(INSTRUCTION_TASK_TWO_IMAGES)
context['display_image'] = instruction_task_second_image
elif step == 9:
first_image = random.choice(PATH_ONE_IMAGES)
PATH_ONE_IMAGES.remove(first_image)
context['display_image'] = first_image
images.insert(0, first_image)
self.request.session['first_image'] = images[0]
self.request.session.get('first_image')
elif step == 10:
second_image = random.choice(PATH_ONE_IMAGES)
PATH_ONE_IMAGES.remove(second_image)
....
....
return context
def done(self, form_list, **kwargs):
return render(self.request, 'Return_to_AMT.html', {
'form_data': [form.cleaned_data for form in form_list],
})
phpMyAdmin
A screenshot of the DB from phpMyAdmin. NOTE: Not the hidden fields at the start are for introduction steps in the survey form, code not shown here for brevity.
Submitting data to a model form does not cause it to be saved automatically. If you want save data from a model form to the database, you need to call its save() method. You could do this in the wizard's done() method.
def done(self, form_list, **kwargs):
# I don't know whether this will work, I am not familiar with your forms.
for form in form_list:
form.save()
# Note the docs suggest redirecting instead of rendering a template.
return render(self.request, 'Return_to_AMT.html', {
'form_data': [form.cleaned_data for form in form_list],
})

Django REST: Cannot add (write) nested item

I am attempting to POST a nested relationship with Django REST Framework (2.3.9), but I am getting this error:
ValueError: Cannot add "": instance is on database "default", value is on database "None"
It seems that Django (1.6) is not recognizing that my HeartbeatSerializer knows anything about ItemSerializer
{
"username":"testing",
"heartbeat": {
"items": [
{
"item_date": "2013-11-24T05:08:12Z",
"item_title": "disagreeing post",
"item_type": "post",
"item_karma": 25
}
]
}
}
Here's the serializers.py:
class ItemSerializer(serializers.ModelSerializer):
'''
Item Serializer
Items are User-generated Content which are captured
from various APIs made available from the Web or
Internet via REST consumed endpoints or Websites
which have been scraped.
'''
class Meta:
model = Item
# fields = ('id', 'item_title', 'item_karma',
# 'item_type', 'item_date', )
class DepthPatchSerializer(serializers.ModelSerializer):
def __init__(self, model=None, **kwargs):
if model is not None:
self.Meta.model = model
super(DepthPatchSerializer, self).__init__(**kwargs);
def get_nested_field(self, model_field):
return DepthPatchSerializer(model=model_field.rel.to)
class HeartbeatSerializer(DepthPatchSerializer):
'''
Heartbeat Serializer
Items are User-generated Content (USG) containers which are used
to consolidate normalized User-generated Interactions (USI).
'''
# items_queryset = Item.objects.all()
# items = ItemSerializer(items_queryset, many=True)
class Meta:
model = Heartbeat
# fields = ('id', 'items', )
depth = 1
class HackerAddSerializer(serializers.ModelSerializer):
'''
User Serializer
Each User is stored in the system itself for analysis and signaling.
We store users such that subsets of user behavior through USG can be
generated and analyzed.
'''
heartbeat = HeartbeatSerializer()
def _save_heartbeat_data(self):
heartbeat_data = self.init_data.get('heartbeat', None)
if heartbeat_data:
hs = HeartbeatSerializer(instance=self.object.heartbeat,
data=heartbeat_data)
import pdb; pdb.set_trace() # XXX BREAKPOINT
if hs.is_valid():
self.object.heartbeat = hs.object
hs.save()
else:
raise Exception(hs.errors)
def save(self):
self._save_heartbeat_data()
password = self.init_data.get('password', None)
confirm = self.init_data.get('confirm', None)
if password and password == confirm:
self.object.set_password(password)
super(HackerAddSerializer, self).save()
return self.object
class Meta:
model = Hacker
exclude = ['last_login', 'password',
'is_superuser', 'is_staff',
'is_active', 'user_permissions',
'groups', ]
Nested Serializers are Readonly at present (out of the box). You have to write your own conversion methods to support writable nested serializers (see docs).
There's work to implement writable nested serializers in the django rest framework project.
Instead, I'd suggest you use a RelatedField (like PrimaryKeyRelatedField or HyperlinkedRelatedField) to reference nested models. You can use the depth option to have these related fields nested when you serialize the data to the client.