Django retrieve all related attributes for model - python

I am writing a store based app. Say theres Store A , Store A can have multiple users, and each user can belong to multiple stores. However, each store has Products and each Product has sizes associated with them.
class Variation(models.Model):
product = models.ForeignKey("Product")
size = models.CharField(choices=SIZES, max_length=5)
pid = models.CharField(unique=True, max_length=100, verbose_name="Product ID")
id_type = models.CharField(max_length=5, choices=ID_TYPES, default="UPC")
price = models.DecimalField(max_digits=5, decimal_places=2)
store = models.ForeignKey("Store")
class Meta:
unique_together = ("store", "pid")
class Product(models.Model):
item = models.CharField(max_length=100, verbose_name="Parent SKU", help_text="reference# - Color Code")
# Google docs sp key
store = models.ForeignKey(Store)
class Store(models.Model):
name = models.CharField(max_length=100)
store_id = models.PositiveIntegerField(unique=True)
admins = models.ManyToManyField(User)
objects = StoreManager()
def __unicode__(self):
return self.name
so i had to write a custom manager for Product in order to filter all products by store, and override the queryset method for this model's admin class, and do so for EVERY attribute belonging to said store. So basically my question is, is there a way to filter all attributes related to a store per store, ex products, tickets, variations
EDIT
This is the product manager so far
class ProductManager(models.Manager):
def active(self, **kwargs):
return self.filter(in_queue=False, **kwargs)
def by_store(self, store=None, **kwargs):
return self.filter(store__id__exact=store, **kwargs)
def from_user(self, request):
qs = self.model.objects.none()
for store in request.user.store_set.all():
qs = qs | store.product_set.filter(in_queue=False)
return qs
so basically, in order to display the products in the change list page, i use the from user method, which returns all the products available to the logged in user

it sounds like what you want to be using is the the django "Sites" framework
add a ForeignKey to Store to point to Site and make it unique.
it may be wise to point Variation and Products' ForeignKeys at Site instead of Store at this point too so that in your views you can filter your results by current site.

Related

Django filter: Many to Many specific field to custom Group field

This is probably not the best way to go about user permissions- though I have a list of User Groups that contain names of Markets and a custom field built in containing the "Market_id".
Within the models I am filtering, I have a ManytoMany relationship linked to another model named "Markets". The "Markets" model also has a "market_id" field.
How can I go about giving the user only the objects that are within their "Market" or group?:
Models I am filtering:
class Opportunity(models.Model):
opportunityid = models.CharField(max_length=500, blank=True)
market = models.ManyToManyField('Market', related_name ='market', blank= True)
Market model- M2M relationship:
class Market(models.Model):
marketname = models.CharField(max_length=500)
market_id = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.marketname
My attempt at the queryset- currently returns a blank queryset, even though my user has permissions for all markets or groups:
class projectViewSet(viewsets.ModelViewSet):
serializer_class = ProjectSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
user = self.request.user
groups = user.groups.all()
finalgroups = []
for g in groups:
finalgroups.append(g.market_id)
queryset = Opportunity.objects.all()
queryset = queryset.filter(market__market_id__in = finalgroups)
return queryset
In case it's not very clear- I am adding the User's to a generic Django User Group- these user groups have a custom field labeled "market_id". See the below screenshots for clarification:
(Within the user page in django-admin)
(Within the group page in django-admin)
Thanks for your time!

Significant performance issue with Django Admin - foreign key labels

I’m experience significant performance issue with Django Admin.
I have a mapping model where I map primary keys of 2 other modes
In my FundManagerMappingAdmin I try to represent the foreign key of the 2 tables with a label from the foreign key models.
The underlying models are about 4000 lines
I’m experiencing slow performance when retrieving the list in admin and then also when editing and updating
Could someone please point out the inefficiencies in this code?
Is there a better way please?
Admin.py
#admin.register(ChampDwDimFundManagerMapping)
class FundManagerMappingAdmin(admin.ModelAdmin):
list_display = ['get_champ_fund_manager_id', 'get_evestment_name', 'get_sharepoint_name', ]
def get_champ_fund_manager_id(self, obj):
return obj.fund_manager_id
get_champ_fund_manager_id.short_description = 'CHAMP Manager ID'
def get_evestment_name(self, obj):
return obj.evestment_fund_manager_id.manager_name
get_evestment_name.short_description = 'Evestment Manager Name'
def get_sharepoint_name(self, obj):
return obj.sharepoint_fund_manager_id.manager_name
get_sharepoint_name.short_description = 'Sharepoint Manager Name'
def get_form(self, request, obj=None, **kwargs):
form = super(ChampFundManagerMappingAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['sharepoint_fund_manager_id'].label_from_instance = lambda obj: "{} {}".format(obj.final_publications_fund_manager_id, obj.manager_name)
form.base_fields['evestment_fund_manager_id'].label_from_instance = lambda obj: "{} {}".format(obj.evestment_fundmanager_id_bk, obj.manager_name)
return form
Models.py
class FundManagerMapping(models.Model):
fund_manager_id = models.AutoField(db_column='FundManagerId', primary_key=True)
sharepoint_fund_manager_id = models.ForeignKey(SharePointFundManager, models.DO_NOTHING, db_column='SharePointFundManagerId')
evestment_fund_manager_id = models.ForeignKey(EvestmentFundManager, models.DO_NOTHING, db_column='eVestmentFundManagerId')
class EvestmentFundManager(models.Model):
evestment_fund_manager_id = models.AutoField(db_column='eVestmentFundManagerId', primary_key=True)
package_execution_id = models.IntegerField(db_column='PackageExecutionId')
evestment_fund_manager_id_bk = models.CharField(db_column='eVestmentFundManagerId_BK', max_length=50)
manager_name = models.CharField(db_column='ManagerName', max_length=255)
class SharePointFundManager(models.Model):
sharepoint_fund_manager_id = models.AutoField(db_column='SharePointFundManagerId', primary_key=True)
package_execution_id = models.IntegerField(db_column='PackageExecutionId')
research_fund_manager_id = models.CharField(db_column='ResearchFundManagerId', max_length=50, blank=True, null=True)
final_publications_fund_manager_id = models.CharField(db_column='FinalPublicationsFundManagerId', max_length=50, blank=True, null=True)
manager_name = models.CharField(db_column='ManagerName', max_length=255)
You are showing the name of related entities (because of get_evestment_name and get_sharepoint_name) without joining/prefetching them. That means for every row that you display and every name of the related entity it requires django to make a database query. You need to override get_queryset() of the ModelAdmin and use select_related to tell django to join those entities from the beginning so that it does not need any additional queries to get those names:
#admin.register(ChampDwDimFundManagerMapping)
class FundManagerMappingAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'sharepoint_fund_manager_id',
'evestment_fund_manager_id',
)
Also you don't name ForeignKey fields something_id. It is just sharepoint_fund_manager because what you get when you call fund_manager.sharepoint_fund_manager_id is not an id but an instance of SharePointFundManager. It is weird to call sharepoint_fund_manager_id.name. An id does not have a name attribute. A fund manager has.
Additionally Django does automatically create a property sharepoint_fund_manager_id for you if you call the field sharepoint_fund_manager to access the plain foreign key.

Django model reference and manipulation

I have the following models in Django that have a structure as follows:
class Office_Accounts(models.Model):
accountid = models.EmailField(max_length=200, unique=True)
validtill = models.DateField(default=datetime.now)
limit = models.CharField(max_length=2)
class Device(models.Model):
device_type = models.ForeignKey(DeviceType,to_field='device_type')
serial_number = models.CharField(max_length=200,unique=True)
in_use_by = models.ForeignKey(User,to_field='username')
brand = models.CharField(max_length=200,default="-", null=False)
model = models.CharField(max_length=200,default="-", null=False)
type_number = models.CharField(max_length=200,blank=True,null=True, default = None)
mac_address = models.CharField(max_length=200,blank=True,null=True, default = None)
invoice = models.FileField(upload_to='Device_Invoice', null=True, blank = True)
msofficeaccount = models.ForeignKey(Office_Accounts, to_field="accountid")
class Meta:
verbose_name_plural = "Devices"
def full_name(self):
return self.device_type + self.serial_number + self.brand
I will display both of the models in admin.py.
Now, I want to display the count of each accountid present in the field "msofficeaccount" (present in Device Models) in my admin page of Office_Accounts model. For an example if xyz#abc.com appears in 10 rows of msofficeaccount field then, the count should be displayed as 10 in Office_Accounts admin page. Can anyone please guide me how should I approach this problem to solve it?
You could add a method to your admin class that returns the count of related devices for each office_account, but that would be very inefficient. Instead you can override get_queryset to annotate the count from a database aggregation function:
from django.db.models import Count
class Office_AccountsAdmin(admin.ModelAdmin):
list_display = (..., 'device_count')
...
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.annotate(device_count=Count('device'))
(On a minor note, Python style is always to use CamelCase for class names, and Django style is to use singular model names, so your model should really be called OfficeAccount.)

Django-Tables2 add extra columns from dictionary

I apologize if this question has been asked before but I couldn't find my specific use case answered.
I have a table that displays basic product information. Product details such as price, number of sales, and number of sellers are scraped periodically and stored in a separate database table. Now I want to display both the basic product information and scraped details in one table on the frontend using tables2. To do this, I wrote a function in my Product model to fetch the latest details and return them as a dictionary this way I can use a single Accessor call.
# models.py
class Product(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=256)
brand = models.ForeignKey(Brand)
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES)
def __unicode__(self):
return self.name
def currentState(self):
currentDetailState = ProductDetailsState.objects.filter(
product=self
).latest('created_at')
# return current details as a dictionary
return {
price: currentDetailState.price,
num_sellers: currentDetailState.num_sellers,
num_sales: currentDetailState.num_sales
}
class ProductDetailsState(models.Model):
product = models.ForeignKey(Product)
created_at = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=6, decimal_places=2, null=True)
num_sellers = models.IntegerField(null=True)
num_sales = models.IntegerField(null=True)
def __unicode__(self):
return self.created_at
# tables.py
class ProductTable(tables.Table):
productBrand = tables.Column(
accessor=Accessor('brand.name'),
verbose_name='Brand'
)
currentRank = tables.Column(
accessor=Accessor('currentRank')
)
class Meta:
model = Product
...
How do I now use this returned dictionary and split it into columns in my Product table? Is there another way to use an Accessor than how I am doing it?
You can use the Accessor to traverse into the dict, so something like this should work:
class ProductTable(tables.Table):
# brand is the name of the model field, if you use that as the column name,
# and you have the __unicode__ you have now, the __unicode__ will get called,
# so you can get away with jus this:
brand = tables.Column(verbose_name='Brand')
currentRank = tables.Column()
# ordering on the value of a dict key is not possible, so better to disable it.
price = tables.Column(accessor=tables.A('currentState.price'), orderable=False)
num_sellers = tables.Column(accessor=tables.A('currentState.num_sellers'), orderable=False)
num_sales = tables.Column(accessor=tables.A('currentState.num_sales'), orderable=False)
class Meta:
model = Product
While this works, sorting is also nice to have. In order to do that, your 'currentState' method is a bit in the way, you should change the QuerySet you pass to the table. This view shows how that could work:
from django.db.models import F, Max
from django.shortcuts import render
from django_tables2 import RequestConfig
from .models import Product, ProductDetailsState
from .tables import ProductTable
def table(request):
# first, we make a list of the post recent ProductDetailState instances
# for each Product.
# This assumes the id's increase with the values of created_at,
# which probably is a fair assumption in most cases.
# If not, this query should be altered a bit.
current_state_ids = Product.objects.annotate(current_id=Max('productdetailsstate__id')) \
.values_list('current_id', flat=True)
data = Product.objects.filter(productdetailsstate__pk__in=current_state_ids)
# add annotations to make the table definition cleaner.
data = data.annotate(
price=F('productdetailsstate__price'),
num_sellers=F('productdetailsstate__num_sellers'),
num_sales=F('productdetailsstate__num_sales')
)
table = ProductTable(data)
RequestConfig(request).configure(table)
return render(request, 'table.html', {'table': table})
This simplifies the table definition, using the annotations created above:
class ProductTable(tables.Table):
brand = tables.Column(verbose_name='Brand')
currentRank = tables.Column()
price = tables.Column()
num_sellers = tables.Column()
num_sales = tables.Column()
class Meta:
model = Product
You can find the complete working django project at github

Django Twitter clone. How to restrict user from liking a tweet more than once?

I'm not sure where to start. Right now, the user can press like as many times they want and it'll just add up the total likes for that tweet.
models.py
class Howl(models.Model):
author = models.ForeignKey(User, null=True)
content = models.CharField(max_length=150)
published_date = models.DateTimeField(default=timezone.now)
like_count = models.IntegerField(default=0)
rehowl_count = models.IntegerField(default=0)
def get_absolute_url(self):
return reverse('howl:index')
def __str__(self):
return self.content
views.py
class HowlLike(UpdateView):
model = Howl
fields = []
def form_valid(self, form):
instance = form.save(commit=False)
instance.like_count += 1
instance.save()
return redirect('howl:index')
Django Twitter clone. How to restrict user from liking a tweet more than once?
As well as tracking how many Likes a post has, you'll probably also want to track who has "Liked" each post. You can solve both of these problems by creating a joining table Likes with a unique key on User and Howl.
The unique key will prevent any User from doing duplicate likes.
You can do this in Django with a ManyToManyField, note that since this means adding a second User relationship to Howl, we need to disambiguate the relationship by providing a related_name
Eg:
class Howl(models.Model):
author = models.ForeignKey(User, null=True, related_name='howls_authored')
liked_by = models.ManyToManyField(User, through='Like')
# ...rest of class as above
class Like(models.Model):
user = models.ForeignKey(User)
howl = models.ForeignKey(Howl)
class Meta:
unique_together = (('user', 'howl'))
like_count count then becomes redundant, since you can use Howl.liked_by.count() instead.
The other benefit of this is that it allows you to store information about the Like - eg when it was added.
An idea could be adding a column to your table named likers and before incrementing like_counts check if the models.likers contains the new liker or not. If not increment the likes, if yes don't.
Changed liked_count in my models.py to
liked_by = models.ManyToManyField(User, related_name="likes")
views.py
class HowlLike(UpdateView):
model = Howl
fields = []
def form_valid(self, form):
instance = form.save(commit=False)
instance.liked_by.add(self.request.user)
instance.like_count = instance.liked_by.count()
instance.save()
return redirect('howl:index')
index.html
{{howl.liked_by.count}}

Categories

Resources