问题
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
回答1:
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 useTrade.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 yourget_context_data()
instead of querying for theTrade
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!
来源:https://stackoverflow.com/questions/61973937/django-filter-queryset-show-variables-on-template