I've been stuck in this for several weeks now and I believe the answer is super simple but somehow I can't find it anywhere online. Which makes me think I'm going about it totally wrong.
All I want to do is be able to filter my stats such as the get_largest_winning_trade function based on the django-filter package. Where am I going wrong? As a side note get_largest_winning_trade is showing the largest winning trade in the Trade Model but it is not being filtered for my criteria. Such as "user".
managers.py
from django.db import models
class TradeQuerySet(models.QuerySet):
def get_users_trades(self, username):
return self.filter(user__username=username)
class TradeManager(models.Manager):
def get_queryset(self):
return TradeQuerySet(self.model, using=self._db)
def get_users_trades(self, username):
return self.get_queryset().get_users_trades(username)
def get_largest_winning_trade(self):
return max([t.profit_loss_value_fees for t in self.all()])
views.py
class StatsView1(LoginRequiredMixin, ListView):
model = Trade
template_name = 'dashboard/stats1.html'
def get_context_data(self, **kwargs):
filter = StatsFilter1(self.request.GET, queryset=self.get_queryset())
context = super().get_context_data(**kwargs)
context['filter'] = filter
context['get_largest_winning_trade'] = Trade.objects.get_largest_winning_trade
return context
stats.html (testing)
filter.qs.get_largest_winning_trade: {{ filter.qs.get_largest_winning_trade }} <br>
Trade.get_largest_winning_trade: {{ Trade.get_largest_winning_trade }} <br>
trade.get_largest_winning_trade: {{ trade.get_largest_winning_trade }} <br>
get_largest_winning_trade: {{ get_largest_winning_trade }} <br> # works but not with filter
Additional Requested Information
Shared the class, it's quite long so I tried to reduce it to what is most helpful. Please let me know if there's anything else.
models.py
class Trade(models.Model):
class Meta:
verbose_name = "Trade"
verbose_name_plural = "Trades"
...
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
status = models.CharField(max_length=2, choices=STATUS_CHOICES, default='cl')
type = models.CharField(max_length=5, choices=TYPE_CHOICES, default=LONG)
broker = models.ForeignKey(Broker, on_delete=models.CASCADE, blank=True, null=True)
asset = models.ForeignKey(Asset, default=DEFAULT_ASSET_ID, on_delete=models.CASCADE, null=True)
#AUTOMATED FIELDS
profit_loss_value_fees = models.FloatField(null=True)
objects = TradeManager()
...
def save(self):
...
self.profit_loss_value_fees = self.get_profit_loss_value_fees()
return super(Trade, self).save()
...
Probably you can do something like this using qs property:
def get_context_data(self, **kwargs):
filter = StatsFilter1(self.request.GET, queryset=self.get_queryset())
context = super().get_context_data(**kwargs)
context['filter'] = filter
context['get_largest_winning_trade'] = filter.qs.get_largest_winning_trade
return context
Update
I think rather than doing it manager, you can do the calculation here with aggregation. Like this:
from django.db.models import Max
...
context['get_largest_winning_trade'] = filter.qs.aggregate(max_value=Max('profit_loss_value_fees'))['max_value']
Reason for using aggregation is to reduce DB hits, because your manager method will hit database multiple times during loop iteration.
Related
I have the following (simplified) Model:
class Order(models.Model):
is_anonymized = models.BooleanField(default=False)
billing_address = models.ForeignKey('order.BillingAddress', null=True, blank=True)
I want to hide the billing_address for objects where the customer chosen to do so by setting is_anonymized=True.
My best approach so far was to do that in the init:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.is_anonymized:
self.billing_address = None
self.billing_address_id = None
This works fine in the Admin BUT...
anywhere in the code there are select_related-QuerySets for Orders:
queryset = Order._default_manager.select_related('billing_address')
All places where the select_related-querysets are used, the billing_address is accidentally shown.
Elsewhere (like in the admin), it isn't.
How can I ensure to remove the billing_address everywhere for objects with is_anonymized = True?
I thought about overwriting the queryset in the manager but i couldn't overwrite the billing_address field by condition.
Using the getter-setter pattern was not a good solution because it breaks the admin at multiple places (there are many attributes to cloak like billing_address).
To be more precise:
The goal is to only simulate as if the data would be deleted although persisting in the database.
I would like to start by saying that I do not understand why would you want to hide information from the admin of your system. Unless you have a complex work environment where only the DBA have access to such information, I honestly do not see the point.
To answer your question...
To hide information in the admin page, one option is to disable all links and replace the HTML with the edit link when is_anonymized value is False:
(adapted from answer_1 and answer_2)
admin.py:
from django.utils.html import format_html
class OrderAdmin(admin.ModelAdmin):
list_display = ['anonymous_address']
def anonymous_address(self, obj):
if not obj.is_anonymized:
return format_html(u'{}', obj.id, obj.billing_address.address)
else:
return ("%s" % ('anonymous'))
def __init__(self, *args, **kwargs):
super(OrderAdmin, self).__init__(*args, **kwargs)
self.list_display_links = None
admin.site.register(Order, OrderAdmin)
Note that with this solution admin still has access to BillingAddress model, if you registered it in the admin site. In that case it will be also necessary to override that.
On your queries, you can aggregate values with conditional expressions:
views.py:
from core.models import Order
from django.db.models import When, Case
def anonymous_address(request):
orders = Order.objects.annotate(anonymised_address=Case(
When(is_anonymized=True, then=None),
When(is_anonymized=False, then='billing_address'),
)).values('is_anonymized', 'anonymised_address')
context = {'orders': orders}
return render(request, 'anonymous_address.html', context)
anonymous_address.html:
{% block content %}
{% for order in orders %}
Should be anonymous: {{order.is_anonymized}} <br>
Address: {{order.anonymised_address}}
<hr>
{% endfor %}
{% endblock content %}
And, instead of having this long query in every view, it is possible to replace that by a custom manager:
models.py:
class AnonymousOrdersManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(anonymised_address=Case(
When(is_anonymized=True, then=None),
When(is_anonymized=False, then='billing_address'),
)).values('is_anonymized', 'anonymised_address')
class Order(models.Model):
is_anonymized = models.BooleanField(default=False)
billing_address = models.ForeignKey(BillingAdress, null=True, blank=True, on_delete=models.CASCADE)
objects = models.Manager()
anonymous_orders = AnonymousOrdersManager()
views.py:
def anonymous_address(request):
orders = Order.anonymous_orders.all()
context = {'orders': orders}
return render(request, 'anonymous_address.html', context)
I want to render every {{ episode.object }} in single video.html page where it works fine for {{ video.object }}. But it isn't showing anything for episode object.. The final template video.html that I want to render episode objects can be seen here https://ibb.co/K9NMXtS
I tried
{% for episode in episodes %}
{{ episode.title }}
{% endfor %}
But that didn't worked. Here is the other configurations:-
#models.py
class Video(models.Model):
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=200, unique=True)
year = models.CharField(max_length=4)
category = models.CharField(max_length=3)
trailer = models.URLField(default='')
def __str__(self):
return self.title
def get_absolute_url(self):
from django.urls import reverse
return reverse("video.html", kwargs={"slug": str(self.slug)})
class Episode(models.Model):
video = models.ForeignKey(Video, related_name='episodes', on_delete=models.CASCADE)
title = models.CharField(max_length=512)
air_date = models.DateField()
videolink = models.URLField(default='')
def __str__(self):
return self.title
# urls.py
urlpatterns = [
path('video/<slug>/', views.VideoDetail.as_view(), name='videos'),
]
# view.py
class VideoDetail(DetailView):
model = Video
template_name = 'video/video.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args,**kwargs)
context['Episodes'] = Episode.objects.all()
return context
Python and Django templates are case sensitive. You use Episodes in the view, which doesn't match episodes in the template. Change one of them so that it matches (episodes is recommended for model instances in Python/Django).
Next, you are using Episode.objects.all() at the moment, which will display all episodes. If you only want the episodes for that video, then filter the queryset:
context['episodes'] = Episode.objects.filter(video=self.object)
Or you can get the same result by following the foreign key backwards:
context['episodes'] = self.object.episodes.all()
add VideoDetail, self in super tag
and while calling in template {{ episode.video.slug }}
and slug enough in url
path('video/<slug>/', views.VideoDetail.as_view(), name='videos'),
class VideoDetail(DetailView):
model = Episode
template_name = 'video/video.html'
def get_context_data(self, *args, **kwargs):
context = super(VideoDetail, self).get_context_data(*args,**kwargs)
context['Episodes'] = Episode.objects.all()
return context
So I'm attempting to create an automated "tick sheet" as a proof of concept for a proposal to automate a process at my workplace.
At work we receive orders from various Suppliers and need to manually tick off the orders that are received against a list of what is expected on a paper sheet each day.
I'm using the generic DetailView in order to have a separate page for each Retailer and have a model representing the Retailer with a ManyToMany relationship with the Suppliers. I also have an Order model to 'simulate' a connection to the actual WMS database (with the intention of modifying it after getting actual db read access if/when this is put into production.)
I need the expected orders (in the Suppliers ManyToMany relationship) to match against those in the 'real' Order model data, and return an answer as to whether it exists in the db (in a way that I can display it on a template).
After a few frantic attempts to solve it myself I'm a little stumped as to how to achieve this within the context of the DetailView, so I fear I am misunderstanding something...
edit: I should have mentioned I only need the 'supplier code' to match but also intend to have the program check for duplicate orders using the 'order reference' once I've figured this out, as without this functionality the whole thing becomes a bit redundant...
My models.py:
from django.db import models
from django.utils import timezone
class Order(models.Model):
''' To simulate connection to main stock db '''
retailer_code = models.CharField(max_length=4)
retailer_name = models.CharField(max_length=100)
supplier_code = models.CharField(max_length=4)
supplier_name = models.CharField(max_length=100)
order_reference = models.CharField(max_length=20)
despatch_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"< {self.order_reference}', {self.supplier_name}, {self.retailer_name} >"
# -------------------------------------------------------------------------------------
class Retailer(models.Model):
retailer_code = models.CharField(max_length=4)
retailer_name = models.CharField(max_length=100)
suppliers = models.ManyToManyField('Supplier')
slug = models.SlugField(unique=True, null=True)
def get_supplier_values(self):
return [(suppliers.supplier_code + ' - ' + suppliers.supplier_name) for suppliers in self.suppliers.all()]
def save(self, *args, **kwargs):
self.slug = self.slug or slugify(self.retailer_code)
super().save(*args, **kwargs)
def __str__(self):
return f"< {self.retailer_code} - {self.retailer_name} >"
class Supplier(models.Model):
supplier_code = models.CharField(max_length=4)
supplier_name = models.CharField(max_length=100)
def __str__(self):
return f"< {self.supplier_code}, {self.supplier_name} >"
My views.py:
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from .models import Retailer, Order
class RetailerListView(ListView):
model = Retailer
context_object_name = 'retailer_list'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Select Retailer'
return context
class RetailerDetailView(DetailView):
model = Retailer
slug_field = 'retailer_code'
slug_url_kwarg = 'retailer_code'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Order Checklist'
context['db_orders'] = Order.objects.filter(retailer_code=self.object.retailer_code)
return context
def do_order_checklist(self):
pass # WIP
Any help would be appreciated...
Probably you can use Exists to to annotate if the order is in DB. For example:
from django.db.models import Exists, OuterRef
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Order Checklist'
squery=self.object.suppliers.filter(supplier_code=OuterRef('supplier_code'))
context['db_orders'] = Order.objects.filter(
retailer_code=self.object.retailer_code
).annotate(
in_db=Exists(squery)
)
return context
Then show in template:
{% for item in db_orders %}
{% if item.in_db %}
// do something
{% else %}
// else
{% endif %}
{% endfor %}
I see, probably the answer you are looking for is this.
as you get the list of supplier_codes in each retailer. instance you already have the list.
retailers_supplier_codes = [1, 2, 3, ...]
matching_orders = Order.objects.filter(supplier_code__in = retailers_supplier_codes)
I would like to get the total amount of followers attached to the models using in models :
class Project(models.Model):
owner = models.ForeignKey(User, related_name='project_created_by', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
description = models.TextField(max_length=150, blank=True, null=True)
followers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followers', blank=True)
created = models.DateTimeField(auto_now_add=True)
last_modefied = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
Here is the class
class ProjectListView(ListView):
template_name = 'projectmanagement/project.html'
context_object_name = 'projects'
def get_queryset(self):
queryset = Project.objects.filter(owner=self.request.user).order_by("name")
return queryset
def get_context_data(self, *args, **kwargs):
context = super(ProjectListView, self).get_context_data(*args, **kwargs)
project = Project.objects.get(pk=12) <-- HERE -->
context['followers'] = project.followers.filter(followers=project).count()
return context
You can .annotate(..) [Django-doc] the queryset of your Product with the number of followers:
from django.db.models import Count
class ProjectListView(ListView):
model = Project
template_name = 'projectmanagement/project.html'
context_object_name = 'projects'
def get_queryset(self):
return super().get_queryset().annotate(
nfollowers=Count('followers')
).filter(
owner=self.request.user
).order_by('name')
Now all projects in the context data will have an extra attribute nfollowers with the number of followers.
You can thus render this for example with:
{% for project in projects %}
{{ project.name }}, followers: {{ project.nfollowers }}<br>
{% endfor %}
I have been searching here and reading the documentation and experimenting in python, but I can't find a solution to my particular mess. I did the Django tutorial, but I'm still confused as to how to pass stuff thru the URL in django when starting to use foreign keys, I'm sure it's pretty simple. I'm a new django user and this is my first post. I have the models Playlist, and PlaylistEntry with mixed relationships to user and videos (not posted). I'm trying to show a detail view that uses a slug of a playlist title, to pull out entries in the playlist. Eg. In python, I can do
entries = PlaylistEntry.objects.filter(playlist__slug='django')
which returns all my entries in the playlist 'Django' correctly.
Here are my models...
class Playlist(models.Model):
class Meta:
verbose_name_plural = 'playliztz'
title = models.CharField(blank=True,
help_text=u'Title of playlist',
max_length=64,
)
user = models.ForeignKey('userprofile.UserProfile',
blank=False,
help_text=u'owns this playlist',
null=False, )
slug = models.SlugField(u'slug',
max_length=160,
blank=True,
editable=False
)
created = models.DateTimeField(editable=False)
modified = models.DateTimeField(editable=False)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('playlist-detail', kwargs={'pk': self.pk})
def save(self):
self.slug = slugify(self.title)
if not self.id:
self.created = datetime.datetime.today()
self.modified = datetime.datetime.today()
super(Playlist,self).save()
class PlaylistEntry(models.Model):
class Meta:
verbose_name_plural = "playlist entries"
video = models.ForeignKey('video.Video',
blank=True,
default=None,
help_text=u'the video title',
null=True, )
playlist = models.ForeignKey('Playlist',
blank=True,
default=None,
help_text=u'Belongs to this playlist',
null=True,)
def __unicode__(self):
return self.video.video_title
My URLS looks like this...
url(r'^playlist/$', PlaylistList.as_view(), name='user_playlists'),
url(r'^playlist/(?P<slug>[0-9A-Za-z-_.//]+)/$', PlaylistDetail.as_view(), name='playlist_entries'),
And my Views.py looks like this...
class PlaylistList(LoggedInMixin, ListView): # shows a list of playlists
template_name = 'userprofile/playlist_list.html'
model = Playlist
context_object_name = 'playlist_list'
def get_queryset(self):
"""Return currently users playlists."""
return Playlist.objects.filter(user__user=self.request.user)
def get_context_data(self, **kwargs):
context = super(PlaylistList, self).get_context_data(**kwargs)
if not self.get_queryset():
context['error'] = "You don't have any playlists yet."
return context
else:
return context
class PlaylistDetail(LoggedInMixin, DetailView):
model = PlaylistEntry
template_name = 'userprofile/playlist_detail.html'
def get_queryset(self):
self.current_playlist = get_object_or_404(Playlist, slug=self.kwargs['slug'])
# CHECK - Prints out correct entries for playlist in slugfield (current_playlist)
self.entries = PlaylistEntry.objects.filter(playlist__title=self.current_playlist.title)
print self.entries
# Should expect to return the same queryset?
return PlaylistEntry.objects.filter(playlist__title=self.current_playlist.title)
def get_context_data(self, **kwargs):
context = super(PlaylistEntry, self).get_context_data(**kwargs)
context['entries'] = PlaylistEntry.objects.all()
return context
self.entries prints the correct entries for this playlist in the Check bit.
In my playlist template I am using a link sending the playlist.slug - the url looks correct like this /user/playlist/this-particular-playlist-slug.
the error is...
Cannot resolve keyword u'slug' into field. Choices are: id, playlist, video
You've made things much more complicated than they need to be.
The model for your detail view should still be Playlist, not PlaylistEntry. The reason you're getting that error is that the slug is on the Playlist model, but you've told the view to filter on PlaylistEntry.
What you actually want to to is to pass the single Playlist identified by the slug into the template. From there, you can easily iterate through the detail objects associated with that playlist via the reverse relation.
So, change that model setting, and drop both get_context_data and get_queryset from the detail view: you don't need them. Then in the template you can simply do:
{% for entry in playlist.playlistentry_set.all %}