Set initial values in form passing parameters (kwargs) with view - django

I want to prefill a form with values taken in a table.
First I pass the PK relative to the line where I wan't to get values and build the kwargs list:
views.py
def NavetteToFicheCreateView(request, pk):
navette = Navette.objects.get(id=pk)
ref = navette.id
attribute_set = navette.famille.pk
cost = navette.cost
qty = navette.qty
etat = navette.etat
etat_ebay = navette.etat.etat_ebay
ean = get_last_ean()
form = NavetteToFicheForm(
request.POST,
ref=ref,
attribute_set=attribute_set,
cost=cost,
qty=qty,
etat=etat,
etat_ebay=etat_ebay,
ean=ean,
)
[...]
then I retrieve the kwargs in the form.py and setup my initial values
class NavetteToFicheForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.ref = kwargs.pop('ref', 'noref')
self.attribute_set = kwargs.pop('attribute_set', 9999)
self.cost = kwargs.pop('cost', 0)
self.qty = kwargs.pop('qty', 0)
self.etat = kwargs.pop('etat', 0)
self.etat_ebay = kwargs.pop('etat_ebay', 9999)
self.ean = kwargs.pop('ean', 9999)
super(NavetteToFicheForm, self).__init__(*args, **kwargs)
self.fields['ref'].initial = self.ref
self.fields['attribute_set'].initial = self.attribute_set
self.fields['cost'].initial = self.cost
self.fields['qty'].initial = self.qty
self.fields['etat'].initial = self.etat
self.fields['etat_ebay'].initial = self.etat_ebay
self.fields['ean'].initial = self.ean
[...]
My problem : some fields like "ref" or "attribute_set" are foreignKeys and are not transmitted when i display the form.
For checking my values :
print(self.ref)
print(self.attribute_set)
output
34
2
noref
9999
questions :
Why does the "print" displays 2 couples of values ? This looks like as if the "noref" and "999" are taken in account.
Why if i set manually 34 and 2 values, it works ?
self.fields['ref'].initial = 34
self.fields['attribute_set'].initial = 2
There's maybe a better way of doing this but I don't know it yet .

Related

Django: How to take my query out of my for loop

I got the following code, that contains N queries:
for qty in total_qty_bought:
product_id = qty["product"]
quantity = int(qty["quantity__sum"])
try:
method_title = (
self.shipment_model.get(order_id=qty["order_id"])
.method_title.replace("Hent-selv", "")
.strip()
)
To solve the issue I tried to take the method_title query out of the for loop like this:
quantity = 0
for qty in total_qty_bought:
quantity = int(qty["quantity__sum"])
method_title = (
self.shipment_model.get(order_id=total_qty_bought[0]['order_id'])
.method_title.replace("Hent-selv", "")
.strip()
)
Note! There will be a full refrence further down, to understand the bigger picture
The issue in my solution is, that I am hard choosing which dict to enter , as I select [0] before order_id, and not in a for loop like before, would be selecting every individual item in the loop.
Is there a more sufficient way to do this? I do not see a solution without the for loop, but django debugger tool tells me it creates 2k+ queries.
CODE FOR REFRENCE
class InventoryStatusView(LoginRequiredMixin, View):
template_name = "lager-status.html"
cinnamon_form = CinnamonForm(prefix="cinnamon_form")
peber_form = PeberForm(prefix="peber_form")
pc_model = InventoryStatus
product_model = Product.objects.all()
order_item_model = WCOrderItem.objects.all()
shipment_model = WCOrderShipment.objects.all()
def get(self, request):
# Get all added objects that hasn't been deleted
objects = self.pc_model.objects.filter(is_deleted=False)
# Get all added objects that has been deleted
deleted_objects = self.pc_model.objects.filter(is_deleted=True)
# Sum all cinnamon that isn't deleted
total_cinnamon = (
self.pc_model.objects.filter(is_deleted=False)
.aggregate(Sum("cinnamon"))
.get("cinnamon__sum", 0.00)
)
# Sum all peber that isn't deleted
total_peber = (
self.pc_model.objects.filter(is_deleted=False)
.aggregate(Sum("peber"))
.get("peber__sum", 0.00)
)
# Get the amount of kilo attached to products
product_data = {}
queryset = ProductSpy.objects.select_related('product')
for productSpy in queryset:
product_data[productSpy.product.product_id] = productSpy.kilo
# Get quantity bought of each product
total_qty_bought = self.order_item_model.values(
"order_id", "product"
).annotate(Sum("quantity"))
# Get the cities from the inventory model
cities = dict(self.pc_model.CITIES)
# Set our total dict for later reference
our_total = {}
product = Product.objects.filter(
product_id__in={qty['product'] for qty in total_qty_bought}
).first()
# Check if we deal with kanel or peber as a product based on slug
index = 0
if product.slug.startswith("kanel-"):
index = 0
elif product.slug.startswith("peber-"):
index = 1
else:
pass
try:
# Sum the total quantity bought
quantity = 0
for qty in total_qty_bought:
quantity = int(qty["quantity__sum"])
# Get the inventory the order is picked from based on shipment method title
method_title = (
self.shipment_model.get(order_id=total_qty_bought[0]['order_id']) # The error
.method_title.replace("Hent-selv", "")
.strip()
)
# If the order isn't picked, but sent, use this inventory
if method_title not in cities.values():
method_title = "Hovedlager"
try:
# Get the total of kanel and peber bought
kilos = quantity * product_data[product.id]
# If there is no inventory, set it to 0 peber and 0 kanel
if method_title not in our_total:
our_total[method_title] = [0, 0]
# Combine place and kilos
our_total[method_title][index] += kilos
except KeyError as ex:
print(ex)
pass
except WCOrderShipment.DoesNotExist as ed:
print(ed)
pass
# Quantities BOUGHT! (in orders!)
print(our_total)
context = {
"cinnamon_form": self.cinnamon_form,
"peber_form": self.peber_form,
"objects": objects,
"deleted_objects": deleted_objects,
"total_cinnamon": total_cinnamon,
"total_peber": total_peber,
"our_total": our_total,
}
return render(request, self.template_name, context)
You can only do one query by using the __in operator:
shipments = self.shipment_model.get(order_id__in=list_containing_order_ids)
Then you can do a normal for loop in which you verify that condition.

django filter queryset show variables on template [duplicate]

This question already has an answer here:
Django-Filter Package: How To Filter Objects To Create Stats And Not Lists
(1 answer)
Closed 2 years ago.
Below is the views.py to my stats page. This page has a bunch of calculations based on my model objects. Works great. However when I apply django-filter to the data it does not change. Example filtering for only "short" trades or in the "last 7 days".
I know that get_context_data is basically hardcoding the results and it will not be affected by any filter query. This is not the approach but I've tried several things with no results so back to square one... How would I do this?
I've tried kwargs.update and context.update but also was not getting results.
This seems like it should be super obvious as how else do people show and filter statistics on objects?
views.py
class StatsView(LoginRequiredMixin, FilterView):
model = Trade
template_name = 'dashboard/stats.html'
filterset_class = StatsFilter
def get_form(self, *args, **kwargs):
form = StatsFilter()
user = self.request.user
form.fields['associated_portfolios'].queryset = Portfolio.objects.filter(user=user)
return form
def get_max_consecutive_wins(self, data):
longest = 0
current = 0
for num in data:
if num > 0:
current += 1
else:
longest = max(longest, current)
current = 0
return max(longest, current)
def get_max_consecutive_loses(self, data):
longest = 0
current = 0
for num in data:
if num < 0:
current += 1
else:
longest = max(longest, current)
current = 0
return max(longest, current)
def get_context_data(self, *args, **kwargs):
trade = Trade.objects.filter(user=self.request.user, status='cl').order_by('created')
all_trades = Trade.objects.filter(user=self.request.user, status='cl').count()
context = super(StatsView, self).get_context_data(*args, **kwargs)
data = [t.profit_loss_value_fees for t in trade]
win_trades_count = [t.trade_result for t in trade].count('win')
loss_trades_count = [t.trade_result for t in trade].count('loss')
scratch_trades_count = [t.trade_result for t in trade].count('scratch')
avg_win = 0 if win_trades_count == 0 else mean(t.profit_loss_value_fees for t in trade if t.trade_result == 'win')
avg_loss = 0 if loss_trades_count == 0 else mean(t.profit_loss_percent for t in trade if t.trade_result == 'win')
avg_win_percent = 0 if win_trades_count == 0 else mean(t.profit_loss_percent for t in trade if t.trade_result == 'win')
avg_loss_percent = 0 if loss_trades_count == 0 else mean(t.profit_loss_percent for t in trade if t.trade_result == 'loss')
context['all_trades'] = all_trades
context['gross_profit'] = sum([t.profit_loss_value for t in trade])
context['net_profit'] = sum([t.profit_loss_value_fees for t in trade])
context['win_trades_profit'] = sum(
t.profit_loss_value_fees for t in trade if t.trade_result == 'win')
context['loss_trades_profit'] = sum(
t.profit_loss_value_fees for t in trade if t.trade_result == 'loss')
context['win_trades_count'] = win_trades_count
context['loss_trades_count'] = loss_trades_count
context['scratch_trades_count'] = scratch_trades_count
context['win_trades_count_ratio'] = win_trades_count / all_trades * 100
context['loss_trades_count_ratio'] = loss_trades_count / all_trades * 100
context['scratch_trades_count_ratio'] = scratch_trades_count / all_trades * 100
context['total_fees'] = sum([t.get_fees() for t in trade])
context['avg_win'] = avg_win
context['avg_loss'] = avg_loss
context['avg_win_percent'] = avg_win_percent
context['avg_loss_percent'] = avg_loss_percent
context['avg_position_size'] = mean(t.position_size for t in trade)
context['largest_winning_trade'] = max([t.profit_loss_value_fees for t in trade])
context['largest_losing_trade'] = min([t.profit_loss_value_fees for t in trade])
context['largest_winning_trade_percent'] = max([t.profit_loss_percent for t in trade])
context['largest_losing_trade_percent'] = min([t.profit_loss_percent for t in trade])
context['max_consecutive_wins'] = self.get_max_consecutive_wins(data)
context['max_consecutive_loses'] = self.get_max_consecutive_loses(data)
context['qs'] = Trade.objects.filter(user=self.request.user, status='cl').order_by('created')
return context
The first problem, like you pointed, is that you are hard-coding the results in the get_context_data(). The FilterView ClassBasedView inherits from the MultipleObjectsMixin mixin of Django, so you should take both into account. I assume from your issues that you are not using the object_list property in your template - that's where the CBV populates the data.
Basically, in your current view, the flow will go as:
get the queryset based in the get_queryset() method.
If this doesn't exist, it will use the queryset property defined
In last case, the model. <-- This is your case, so it will use Trade.objects.all()
the FilterSet is applied to the original queryset
paginate (if you have it configured) and assign the filtered queryset to the object_list in the context.
So, some changes you can start trying:
Use the object_list to perform the stats calculation in your get_context_data() instead of querying for the Trade model yourself.
If you are always going to filter the results based in the current user, you can use the get_queryset() method to apply that filter from there, so you would do:
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(user=self.request.user, status='cl')
I would also recommend you to look at Django's built-in data aggregation mechanisms, as it would make all those calculations at DB level. If your start to treat big amounts of data, making all this manual processing is going to be an issue.
I think those points would be a good start!

For loop in Django

I have two question. First question: Python script that shows me the precipitation for a certain period.For example, I'm getting an initial year-month and a final year-month.
Initial:
year:2000
month:3
Final
year1:2005
month:4
Now, instead of seeing:
2000/3,2000/4,2000/5,2000/6..........2005/1,2005/2,2005/3,2005/4
she works like this(look in the hooked picture):
2000/3, 2000/4, 2001/3, 2001/4........2005/3,2005/4.
I want to work for me like the first case.
def period_month_prec(year,month,year1,month1):
for i in range (year,year1+1,1):
for j in range(month,month1+1,1):
......................
Second question: How to write the output(picture) from the script in csv.fileenter image description here
This is what my views.py script looks like , which saves me only the first result:
def monthly_period(request):
if request.method == "POST" :
form = PeriodMonthlyForm(request.POST)
if form.is_valid():
data = form.cleaned_data
year = data.get('year')
month = data.get('month')
year1 = data.get('year1')
month1 = data.get('month1')
lon = data.get('lon')
lat = data.get ('lat')
inter = data.get('inter')
point = period_month_prec(year,month,year1,month1,lon,lat)
args = {'point':point}
response = HttpResponse(content_type='text/txt')
response['Content-Disposition'] = 'attachment; filename="precipitation.txt"'
writer = csv.writer(response)
writer.writerow([point])
return response
else:
form = PeriodMonthlyForm()
active_period_monthly = True
return render (request, 'carpatclimapp/home.html',{'form':form, 'active_period_monthly': active_period_monthly})
Ok, i have forms like this:
Forms
You set initial values(red color) and end interval(blue color). For this given interval, the lon and lat are defined for the point in which we want to perform interpolation. When you press the submit button, it starts with interpolation for a defined period. The loop problem is because it works only for the defined months (we see from the 2nd picture that it only works in the interval 1-6) but not for 7,8,9,10,11,12 months between these years.
Initial: year:2000, month:3
Final: year1:2001, month:4
for this she's doing it like this: 2000/3,2000/4,2001/3,2001/4
I do not want that, I want this: 2000/3,2000/4,2000/5,2000/6,2000/7.....2000/12,2001/1,2001/2,2001/3,2001/4.
this is me code :
def period_month_prec(year,month,year1,month1,lon,lat):
cnx = sqlite3.connect(DB1)
cursor = cnx.cursor()
table = 'monthly'
year = int(year)
year1 = int(year1)
month = int(month)
month1 = int(month1)
for i in range (year,year1+1,1):
for j in range(month,month1+1,1):
query = '''
SELECT dates, cell, prec FROM %s WHERE dates = "%s-%s" ;
''' % (table,i,j)
df = pd.read_sql_query(query, cnx)
tacka = '''SELECT id, lon, lat,country,altitude FROM %s;''' % 'grid1'
grid1 = pd.read_sql_query(tacka, cnx)
podaci = pd.merge(df,grid1,left_on='cell',right_on='id')
podaci_a = podaci.drop(['cell','id','country','altitude'],axis=1)
lon_n = podaci_a['lon'].values
lat_n = podaci_a['lat'].values
prec =podaci_a['prec'].values
x_masked, y_masked, prec_p = remove_nan_observations(lon_n, lat_n, prec)
xy = np.vstack([x_masked,y_masked]).T
xi = ([lon,lat])
inter_point = interpolate_to_points(xy,prec_p,xi, interp_type='linear'
return (i,j,lon,lat,inter_point)
The results that come out look like this:
loop with calculations
The second question was how to save these results(2nd picture) in the csv file, how to write correctly views.py. Currently she looks like this :
def monthly_period(request):
if request.method == "POST" :
form = PeriodMonthlyForm(request.POST)
if form.is_valid():
data = form.cleaned_data
year = data.get('year')
month = data.get('month')
year1 = data.get('year1')
month1 = data.get('month1')
lon = data.get('lon')
lat = data.get ('lat')
inter = data.get('inter')
point = period_month_prec(year,month,year1,month1,lon,lat)
args = {'point':point}
response = HttpResponse(content_type='text/txt')
response['Content-Disposition'] = 'attachment; filename="precipitation.txt"'
writer = csv.writer(response)
writer.writerow([point])
return response
else:
form = PeriodMonthlyForm()
active_period_monthly = True
return render (request, 'carpatclimapp/home.html',{'form':form, 'active_period_monthly': active_period_monthly})
I hope I'm a little clearer now

Handling dynamic form submit in django forms

I'm generating a form based on meta data in the following manner
class MeasureForm(forms.Form):
def __init__(self,measure_id,*args,**kwrds):
super(MeasureForm,self).__init__(*args,**kwrds)
m = Measure.objects.get(pk=measure_id);
if (m):
self.fields["measure_id"] = forms.IntegerField(initial = m.id , widget=forms.HiddenInput())
for mp in MeasureParameters.objects.filter(measure = m):
# get the NVL'ed copy of the parameter
p = mp.get_parameter_for_measure()
if not p.is_modifiable:
# the file has a constant value
if (p.values and p.default): # contant must have both values and default index
value_ = p.values[p.values.keys()[p.default-1]];
self.fields[p.name] = forms.IntegerField(label = p.description , initial = value_);
self.fields[p.name].widget.attrs['readonly'] = True;
else:
raise Exception("Parameter set as unmodifiable but has no value. [measure: %s, parameter: %s, measureparameter %s]"
% (measure_id , p.id , mp.__unicode__()))
elif (p.values):
# convert hstore dict to list of tuples for the choices to read
values_ = [(v, k) for k, v in p.values.iteritems()];
# set default if exists , else take the first item
default_ = values_[p.default-1][0] if p.default else values_[0][0]
self.fields[p.name] = forms.ChoiceField(label = p.description , choices = values_ , initial = default_)
else:
self.fields[p.name] = forms.IntegerField(label = p.description)
if (not p.is_visible):
self.fields[p.name].widget = forms.HiddenInput()
else:
raise Exception ("Could not find measure. [measure %s]" % (measure_id));
This is my view
def index(request,measure_id = None):
owners = Owner.objects.all()
form = None
result = None
title = None
# handle the form
if request.method == 'POST': # the form has been submitted
form = MeasureForm(request.POST)
if form.is_valid(): # All validation rules pass
result = 100;
else:
if (measure_id):
title = Measure.objects.get(pk=measure_id).name;
# make an unbound form
form = MeasureForm(measure_id)
return render(request, 'calc/index.html' ,
{'owners' : owners,
'form' : form ,
'title' : title ,
'result' : result})
I followed this tutorial.
I'm running into a problem when i issue submit back to the index view. i get the following error
int() argument must be a string or a number, not 'QueryDict'
I can see in the dump that it's fail in this line
m = Measure.objects.get(pk=measure_id);
So I looked around and found this post . I tried to change the call to the form like this
form = MeasureForm(request.POST, measure_id = request.POST.get('measure_id'))
and i got the following error
__init__() got multiple values for keyword argument 'measure_id'
My first question is - Why is django executing the init again. a form object was already created - the fields just need to get bound form the post data.
Second , how can I overcome this ?
**django newbie. Thanks.
Remove measure_id from init method and instead use kwrds['measure_id'] to retrieve it inside, then use them appropriately for keyword argument:
MeasureForm(request.POST, measure_id=measure_id)
MeasureForm(measure_id=measure_id)

Django Custom Model Field with Validation...how to hook it back to ModelForm

A common occurrence I have with one particular project is that it requires the user to enter dimensions (for width/depth/height) in Feet and Inches. Calculations are needed to be performed on that dimension, so I've been working on a custom field type that takes in a dimension in Feet/Inches (eg. 1'-10") and saves it to the database as a decimal number using a regex to parse the input. The field displays to the end-user as feet-inches at all times (with the eventual goal of writing a
method to be able to optionally display in metric, and interact with measure.py, and geodjango stuff). What I have so far is definitely not DRY, but aside from that, I'm having trouble with validation at the form level. The custom model field itself works properly (from what I've seen), and I've written a form field clean method which should work to validate the field. My question is how to hook that form field back into my model form to work for all the width/depth/height fields.
I'm thinking maybe an override of the init on the modelform (a la self.fields['depth']...) , but I'm not quite sure where to go from here...
DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$')
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$')
class FtInField(models.Field):
__metaclass__ = models.SubfieldBase
empty_strings_allowed = False
def db_type(self):
return 'double'
def get_internal_type(self):
return "FtInField"
def to_python(self,value):
if value is u'' or value is None:
return None
if isinstance(value, float):
m = FTDCML_PATTERN.match(str(value))
if m is None:
raise Exception('Must be an integer or decimal number')
feet = int(m.group('feet'))
dec_inch = float(m.group('dec_inch') or 0)
inch = dec_inch * 12
return "%d\'-%.0f\"" % (feet,inch)
return value
def get_db_prep_value(self,value):
if value is u'' or value is None:
return None
m = FTIN_PATTERN.match(value)
if m is None:
raise Exception('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
return (feet + (inch/float(12)))
class FtInField(forms.Field):
def clean(self,value):
super(FtInField, self).clean(value)
if value is u'' or value is None:
raise forms.ValidationError('Enter a dimension in X\'-Y" format')
m = FTIN_PATTERN.match(value)
if m is None:
raise forms.ValidationError('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
value = '%d\'-%.0f"' % (feet,inch)
return value
class ProductClass(models.Model):
productname = models.CharField('Product Name', max_length=60,blank=True)
depth = FtInField('Depth (Feet/Inches)')
width = FtInField('Width (Feet/Inches)')
height = FtInField('Height (Feet/Inches)')
class ProductClassForm(forms.ModelForm):
depth = FtInField()
width = FtInField()
height = FtInField()
class Meta:
model = ProductClass
class ProductClassAdmin(admin.ModelAdmin):
form = ProductClassForm
Thank you, thank you to both of you. This is what I came up with (based on both of your advise). I'll work on defining a data type to make it better in terms of repetition, but in the meantime, this works...(I was so close, yet so far away...) You guys are amazing. Thanks.
DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$')
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$')
class FtInFormField(forms.Field):
def clean(self,value):
super(FtInFormField, self).clean(value)
if value is u'' or value is None:
raise forms.ValidationError('Enter a dimension in X\'-Y" format')
m = FTIN_PATTERN.match(value)
if m is None:
raise forms.ValidationError('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
value = '%d\'-%.0f"' % (feet,inch)
return value
class FtInField(models.Field):
__metaclass__ = models.SubfieldBase
empty_strings_allowed = False
def db_type(self):
return 'double'
def get_internal_type(self):
return "FtInField"
def to_python(self,value):
if value is u'' or value is None:
return None
if isinstance(value, float):
m = FTDCML_PATTERN.match(str(value))
if m is None:
raise Exception('Must be an integer or decimal number')
feet = int(m.group('feet'))
dec_inch = float(m.group('dec_inch') or 0)
inch = dec_inch * 12
return "%d\'-%.0f\"" % (feet,inch)
return value
def get_db_prep_value(self,value):
if value is u'' or value is None:
return None
m = FTIN_PATTERN.match(value)
if m is None:
raise Exception('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
return (feet + (inch/float(12)))
def formfield(self, **kwargs):
defaults = {'form_class': FtInFormField}
defaults.update(kwargs)
return super(FtInField,self).formfield(**defaults)
class ProductClass(models.Model):
productname = models.CharField('Product Name', max_length=60,blank=True)
depth = FtInField('Depth (Feet/Inches)')
width = FtInField('Width (Feet/Inches)')
height = FtInField('Height (Feet/Inches)')
class ProductClassForm(forms.ModelForm):
class Meta:
model = ProductClass
class ProductClassAdmin(admin.ModelAdmin):
form = ProductClassForm
To avoid duplication you should probably implement a data type class that handles the parsing of feets and inches for you, it should greatly simplify the other code.
Then you should create a model field and form field, keeping in mind that these are two COMPLETELY SEPARATE components. (which you more or less have already done, but this is just for completeness)
Now, if I'm reading the question right, you want to set the default form field for your model field. To facilitate this you want to implement the formfield() function on your model field class. Ref: the django docs