I've got simple project, that do almost nothing now - there is MultipleChoiceField with some Person-values and when user selects some values and clicks "submit" button - I'm sarching for Houses, where selected guys lived.
There is about 1.5k persons and 20k houses in my database. If I select 3+ people from list (in this case, there is about 5-6k values in 'houses' list), processing time is taking very long time (~5-7 seconds for my 't' variable).
As django-debug-toolbar says, DB-queries takes only 0.7 seconds and ~95% of time is 'request' section of 'Timer' panel. So the question is - what am I doing wrong? I think there is wrong way of using render_to_response. Can you recommend me something to optimize my code? I'm new at Django, thank you.
At my 'guess_houses.html' I only display list of houses with their field.
Here is my views.py:
class HouseView(FormView):
template_name = 'guess_houses.html'
form_class = PersonListForm # that's my MultipleChoiceField with persons in it
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
persons_id = form.cleaned_data['persons']
houses= []
t1 = time()
for id_person_dict in House.objects.all().values('id', 'persons', 'address'):
# if persons_id and persons who lived in house got intersection element(s)
if set(persons_id) & set(id_person_dict['persons']):
if id_person_dict not in houses:
houses.append(id_person_dict)
return render_to_response(self.template_name, {'form': form, 'houses': houses, 't': time()-t1})
Related
I am new to Django Class Based Views and I am working on a project where on the template I want to have Form for creating customer accounts on the left and list of existing customers on the right.
So far I have the list of existing customers displayed but for the form I don't know how to pass its variable context to the same template, or it is not possible to Pass a Form that would be submitted inside a ListView Method. And I also want to generate unique account numbers of 10 Digits in ModelForm which I want the form field to be auto-filled and disabled
Here is my form code:
import secrets
#I want to Generate Account Number of 10 Digits but getting only 2
account = secrets.randbits(7)
#class for Customer Account Form
class CustomerAccountForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().init(*args, **kwargs)
self.fields['accountnumber'].initial = account
class Meta:
model = Customer
fields = ['accountnumber','surname','othernames','address','phone']
Code for my views (ListView)
class CustomerListView(ListView):
model = Customer
form_class = CustomerAccountForm
template_name = 'dashboard/customers.html'
#Function to get context data from queries
def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
#Get Day of today from current date and time
now = datetime.datetime.now()
#Get the date today
date_today = datetime.datetime.now().date
#Count Number of Withdrawals Today and passing in context
context_data['count_withdrawals_today'] = Witdrawal.objects.filter(date__year=now.year, date__month=now.month, date__day=now.day).count()
context_data['count_deposits_today'] = Deposit.objects.filter(date__year=now.year, date__month=now.month, date__day=now.day).count()
context_data['count_accounts'] = Customer.objects.count()
context_data['count_users'] = User.objects.count()
#Calculate today Deposit Today
context_data['total_deposit']= Deposit.objects.filter(date__year=now.year, date__month=now.month, date__day=now.day).aggregate(total_deposit=Sum('deposit_amount')).get('total_deposit') or 0
#Calculate today Withdrawal Today
context_data['total_withdrawal']= Witdrawal.objects.filter(date__year=now.year, date__month=now.month, date__day=now.day).aggregate(total_withdrawal=Sum('withdrawal_amount')).get('total_withdrawal') or 0
return context_data
Someone should please help me on how this is properly done and the form would be submitted successfully. Thanks in anticipation for your answer.
secrets.randbits(k) generates a random integer within a bit range.
For k=4 then Unsigned integer From 0 to 15.
For k=8 then Unsigned integer From 0 to 255.
For k=16 then Unsigned integer From 0 to 65,535, and so on.
If you want 10 random digits then you can use for example:
import random
account = "".join(str(random.randint(0, 9)) for _ in range(10))
After going through many tutorials and blogs on Django Class Based Views with ListViews for Forms, I discovered that ListViews was designed to populate Model items while FormViews is designed for creating and processing forms both can't be used on one template. Although many developers have had a way around it with the use of Multiple Mixins but no best practice has be mentioned for or against their findings yet.
In these case, I concluded that for a Django Template to be able to process Model Form and at the same time Populate Database items, it needs not to use Class Based Views but Function Based Views.
I want to display a list of all payments done by the company. But I want to be able to filter the dates, only showing payments for example:
In the last 30 days
In the last 90 days
Between two specific dates
Now, I know I can filter in my views between two dates, or before today for example.
But how I can I do this using something like a dropdown or a Data chooser on the template?
Something like this for reference.
For reference, the payments model would be this:
class Payment(models.Model):
name = models.CharField(max_length=250)
date = models.DateField(default=datetime.now)
value = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0.00)])
Use django forms with choice field and based on the value filter your queryset. e.g:
Choices:
PERIOD_LAST_THREE_MONTHS = "last-three-months"
PERIOD_LAST_SIX_MONTHS = "last-six-months" # ..
PERIOD_CHOICES = (
(PERIOD_LAST_THREE_MONTHS, _("Last 3 Months")),
(PERIOD_LAST_SIX_MONTHS, _("Last 6 Months")), ..
)
NOTE: variables separated with hyphens because they will be used in query string of http request.
Utils:
def get_period_date(period):
if period == PERIOD_LAST_THREE_MONTHS:
return timezone.now() - timezone.timedelta(days=90)
elif period == PERIOD_LAST_SIX_MONTHS:
return timezone.now() - timezone.timedelta(days=180)
This function will return a datetime object to filter your queryset with.
Forms:
class DateForm(forms.Form):
period = forms.ChoiceField(choices=PERIOD_CHOICES, required=False)
View:
class PaymentFormView(base.TempleView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['payments'] = Payment.objects.filter(date__gte=get_period_date(self.request.GET('period'))
return context
So basically based on the form value we get a date time object and filtered the queryset(Remember you have a date object). I have used template view instead of form view because I assume you do not want to accept post request. But again you can use form view and not allow post methods, this is up to you(It can be also done with function based views). Also consider the possibility that form can be empty so query string in http request may not have the period argument that may cause error and handle this situations how every you like.
I am trying to create a SessionWizardView for a trip creation process. The trip might have one leg (one way) or two legs (round trip). Each leg has similar schema so I would like to use the same Form for both step 0 and step1, with a condition saying only to use step1 when the flight is round trip.
The problem I am having is that my "submit" button keeps loading step 0 over and over again instead of moving on to step 1 as it should for a round trip flight. (I am prepopulating each of the forms based on previously requested trip info for each leg in the get_form_initial() override). My form populates correctly for the first leg, it just populates the first leg data on every submit ad infinitum.
I could make two identical forms, but that seems like poor practice. Slightly better, I could have the Return trip form just inherit from the Outbound trip form and not make any changes to it - this is what I'll try next barring a better solution.
But, I'm really wondering if there is there a way to use the same form twice?
In my urls.py:
wizard_forms = [TripCreationForm,TripCreationForm]
urlpatterns = patterns('',
url(r'^trip/wizard/(?P<pk>\d+)$',
views.CreateTripSetView.as_view(wizard_forms,
condition_dict= {'1':show_return_trip_form}), name='admin_add_tripset')
)
in views.py:
def show_return_trip_form(wizard):
"""
Tells the CreateTripSetView wizard whether to show the return trip form
Args:
wizard:
Returns: True if this is a round trip, false if one-way
"""
cleaned_data = wizard.get_cleaned_data_for_step('0') or {}
if cleaned_data.get('total_legs') == 2:
return True
return False
class CreateTripSetView(SessionWizardView):
def get_form_initial(self, step):
"""
Populates the initial form data based on the request, route etc.
THIS IS ALWAYS FIRING FOR STEP=0 WHEN I HIT SUBMIT.
Args:
step:
Returns:
"""
initial = self.initial_dict.get(step, {})
triprequest = TripRequest.objects.filter(id=self.kwargs['pk']).first()
if triprequest is None:
return initial
initial.update({
'request_id': flight_request.id,
#other fields set on initial here
})
return initial
in forms.py:
class TripCreationForm
#field defs ex.
request_id = forms.IntegerField()
#etc.
def __init__(self, initial, *args, **kwargs):
object_data = {}
object_data['request_id'] = initial['request_id']
#etc.
super(AnywhereFlightCreationForm, self).__init__(initial=object_data, *args, **kwargs)
Edited:
So far I've been able to make this work using two subclasses of TripCreationForm but not using TripCreationForm for both.
Thanks in advance!
The wizard needs to identify them as separate steps. Maybe this would work?
wizard_forms = [
("form1", TripCreationForm),
("form2", TripCreationForm),
]
using CreateView class, I want to save multiple data entries.
example inputs:
item is "apple,banana,carrots"
location is "location 1"
I want to save them to database like this:
[apple, location 1]
[banana, location 1]
[carrots, location 1]
#model.py
class Inventory(models.Model):
item = models.CharField(max_length=14)
location = models.CharField(max_length=10)
#forms.py
class InventoryCreateForm(forms.ModelForm):
item = forms.CharField(widget=forms.Textarea(attrs={'rows': 8,
'cols': 14}))
class Meta:
model = Inventory
#views.py
class InventoryCreateView(CreateView):
model = Inventory
form_class = InventoryCreateForm
Thank you
You need to override the "form_valid()" method used by the createview.
You then need to read in the form data
def form_valid(self,form):
self.object = form.save(commit=False)
foo = self.object.bar #your data is in the object
Then because you are using a textfield you need to somehow split the data passed into the form and loop over those values. Ideally you will want a list of items ['apple', 'banana', 'pear']
then take the location out of the list and store that into a variable that can be used later on location_variable.
Once you have the data in the form you want you then need to instantiate the Inventory model
from foo.models import Inventory #import at the top of your file
for item is list:
inventory = Inventory()
inventory.item = item
inventory.location = location_variable
inventory.save()
I hope this answer can help you in some way, if you would like further details on Class based view, visit ccbv where all of the information for each view is listed.
Otherwise you can look in the django Form docs for a more suitable form to use.
hopefully this helps anyone else who comes to this page.
Here is what I did:
class TrainingBulkCreateForm(ModelForm):
# todo: could we make the original user object a multiple choice field instead?
extra_user = forms.ModelMultipleChoiceField(queryset=User.objects.filter(is_active=True), required=True)
class Meta(object):
model = Training
fields = '__all__'
def post(self, request, *args, **kwargs):
result = super().post(request, *args, **kwargs)
users = request.POST.getlist('extra_user')
if users:
# modify request.POST data then re-save the additional users
for user in users:
# Need to copy the data because the initial QueryDict is immutable data
postdata_copy = request.POST.copy()
postdata_copy['user'] = user
form2 = TrainingBulkCreateForm(postdata_copy)
form2.save()
return result
I have my item field (mine is user) split into two form fields - the original item which is 1 object like apple.
Then I also have extra_user as a second form field. This takes multiple objects like [banana, carrots]
In the view I invoke the super().post(...) to save the initial apple object.
Then I check for the extra_user field to see if there are multiple foods. If there are extras, I copy the immutable QueryDict object request.POST as postdata_copy.
Then I modify postdata_copy so instead of having apple we replace with banana. (This basically just duplicates the apple form into a new copy with banana and re-saves the form). Then we loop again and replace apple with carrots.
Note that my Form object uses ModelMultipleChoiceField for extra_user object. This has better data integrity than typing in raw text.
I got 2 pages. Step 2 and Step 3. What I'm trying to do is pass the selected object from the step 2 form, to the step 3 form so I can filter the objects of the Step 3 forms. You can see the 2 pages/forms in the images below. So when a user selects a university in step 2, then the step 3 must show only the courses of the selected university.
My current code is really simple since I'm deleting and re-writing the code for the past days without results.
views.py
def step2(request):
universities = University.objects.order_by('name').distinct()
return render_to_response("registration/step2.html", {'universities': universities}, RequestContext(request))
def step3(request):
courses = Course.objects.order_by('name')
return render_to_response("registration/step3.html", {'courses': courses}, RequestContext(request))
In your view, you have to retrieve the selection that the user made and use it to filter the choices for the next form. Something like:
form = FirstForm(request.POST)
if form.is_valid():
uni = form.cleaned_data['uni']
courses = Course.objects.filter(university__name=uni).order_by('name')
return render_to_response("registration/step3.html", {'courses': courses}, RequestContext(request))