django many to many field - programmatically retrieve relations - django

I have been working on a model for tags and am trying to avoid using contenttypes. I have couple questions related to ManyToManyField in django.
I have the following model
taggables/models.py
class Tag(models.Model):
tag_statuses = (
(u'P', _('Pending approval')),
(u'A', _('Approved')),
)
slug = models.SlugField()
created_at = models.DateTimeField(null=True, blank=True)
created_by = models.ForeignKey(User, related_name='tagged_item_created_by')
status = models.CharField(max_length=20, choices=tag_statuses)
site = models.ForeignKey(Site, default=settings.SITE_ID, related_name='tagged_item_site')
def __unicode__(self):
return self.slug
class TagI18n(models.Model):
tag = models.CharField(max_length=100)
descriptor = models.TextField(null=True, blank=True)
# i18n properties
item = models.ForeignKey(Tag)
language = models.CharField(max_length=6, choices=settings.LANGUAGES, default=settings.LANGUAGE_CODE)
class Meta:
unique_together = (("language", "item"))
def __unicode__(self):
return self.tag
I also have different apps around my project that uses tag model as many to many field. such as events for example
evetns/models.py
class Item(models.Model):
event_status_list = (
(u'P', _('Pending approval')),
(u'A', _('Approved')),
(u'R', _('Rejected')),
(u'S', _('Spam')),
)
published_at = models.DateTimeField(null=True, blank=True)
published_by = models.ForeignKey(User, null=True, blank=True, related_name='item_published_by')
updated_by = models.ForeignKey(User, null=True, blank=True, related_name='item_updated_by')
updated_at = models.DateTimeField(null=True, blank=True)
site = models.ForeignKey(Site, default=settings.SITE_ID, related_name='events_item_site')
event_slug = models.SlugField(null=True, blank=True)
# event timing
event_start_date = models.DateField()
event_start_time = models.TimeField(null=True, blank=True)
event_end_date = models.DateField()
event_end_time = models.TimeField(null=True, blank=True)
event_recurrent = models.BooleanField(default=False)
event_status = models.CharField(max_length=20, choices=event_status_list, default=u'P')
# relations
media = models.ManyToManyField(ImageFile, null=True, blank=True)
comments = models.ManyToManyField(Comment, null=True, blank=True)
votes = models.ManyToManyField(Vote, null=True, blank=True)
tags = models.ManyToManyField(Tag, null=True, blank=True)
audience = models.ManyToManyField(Audience, null=True, blank=True)
Now what am trying to do here is run a query to programmatically retrieve all the related models to Tag and then count how many a times a tag was used. Am sure I can do that with contenttypes (generic types) but I don't know how it will perform under heavy usage that's why I wanted to do the many to many fields.

If you are interested in the total number of usage ( aka reference count ) of a tag very often, I think you should store it in the database, example put one extra field to the Tag model, like
referencecount = models.IntegerField( default=0 )
Than in the appropriate places, ( example models .save() )you can increment or decrements it's value.

For your use case, the performance of generic wouldn't matter, because you need anyway to do N queries over 2N tables (one for each "taggable" model and one for each m2m join table, at least).
With the m2m approach, you should have the list of 'taggable' models stored somewhere, at least as a list of ('app_name', 'model') pairs. Then use ContentType (it's very performant) to get the actual model class or query directly from there:
counts = {}
for m in taggable_models:
ct = ContentType.get_by_natural_key(*m)
c = ct.model_class().objects.filter(tags=yourtag).distinct().count()
counts[ct.name] = c

Related

How do I show only a subset of options in a Django dropdown menu

I have an app that allows users to signup and register for courses (from a 'TrainingInstance' model). These events have names etc and are categorised as Past or Current in the database (in the 'Training' model). When I show the BuildOrderForm in my template, I want only options for Current trainings to be shown in the dropdown menu. How can this be done in Django without javascript or Ajax?
I have the following form in forms.py:
class BuildOrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['training_registered']
And the following models in models.py:
class Training(models.Model):
""" Model which specifies the training category (name) and whether they are Past or Present"""
YEAR = (
('current', 'current'),
('past', 'past'),
)
name = models.CharField(max_length=200, null=True)
year= models.CharField(max_length=200, null=True, choices=YEAR, default='current')
def __str__(self):
return self.name
class TrainingInstance(models.Model):
""" Creates a model of different instances of each training ( May 2021 etc) """
name = models.CharField(max_length=200, null=True, blank=True)
venue = models.CharField(max_length=200, null=True, blank=True)
training = models.ForeignKey(Training, on_delete= models.CASCADE, null = True)
training_month = models.CharField(max_length=200, null=True, blank=True)
participant_date = models.CharField(max_length=20, null=True, blank=True)
staff_date = models.CharField(max_length=20, null=True, blank=True)
graduation_date = models.CharField(max_length=200, null=True, blank=True)
def __str__(self):
return self.name
class Order(models.Model):
REGSTATUS = (
('registered', 'registered'),
('enrolled', 'enrolled'),
('holding', 'holding'),
('withdrawn', 'withdrawn'),
('waiting', 'waiting'),
)
customer = models.ForeignKey(Customer, on_delete= models.CASCADE, null = True)
training_registered = models.ForeignKey(TrainingInstance, on_delete= models.SET_NULL, blank = True, null = True)
registration_date = models.DateTimeField(null=True,blank=True)
regstatus = models.CharField(max_length=200, null=True, choices=REGSTATUS, default='registered')
def __str__(self):
return self.customer.username
Here is what I have done - which works but I'm also open to feedback about good/bad practice.
class BuildOrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['training_registered']
def __init__(self,*args,**kwargs):
super (BuildOrderForm,self ).__init__(*args,**kwargs)
self.fields['training_registered'].queryset = TrainingInstance.objects.filter(training__year ="current")

How to add ArrayField in Django?

my models.py
class LiveClass_details(models.Model):
standard = models.ForeignKey(LiveClass, on_delete=models.CASCADE)
chapter_details = models.TextField(default='')
mentor_id = models.ForeignKey(Mentor, max_length=30, on_delete=models.CASCADE)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
doubtClass = models.OneToOneField(DoubtClasses, on_delete=models.PROTECT, null=True, blank=True)
isDraft = models.BooleanField(default=True)
ratings = models.FloatField(default=0)
no_of_students_registered = models.IntegerField(default=0)
# registered_students = models.ManyToManyField(RegisteredNames, null=True, blank=True)
no_of_students_attended = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'LiveClass_details'
class RegisteredNames(models.Model):
name = models.CharField(max_length=100, unique=True)
liveclass_id = models.ForeignKey
I am creating a endpoint where when a user register himself his name will get added to registered_students , so i had made a registered students ManyToMany Field hoping it will get updated when a user is registered but then i understand that it will contain all the names that are present in the RegisteredNames Model meaning names registered across all the liveclasses but i want only the names that are registered for a particular liveclass in the field so i need a array like field which i think is not possible so please help me in improving my logic, how can i achieve it
The documentation and django tutorials are very good: https://docs.djangoproject.com/en/3.2/topics/db/models/ https://docs.djangoproject.com/en/3.2/intro/tutorial02/#creating-models
Your code is very close. You don’t need the many-to-many field, and you need to specify the type of the Foreign key relationship in the RegisteredNames. You can do this:
class LiveClass_details(models.Model):
standard = models.ForeignKey(LiveClass, on_delete=models.CASCADE)
chapter_details = models.TextField(default='')
mentor_id = models.ForeignKey(Mentor, max_length=30, on_delete=models.CASCADE)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
doubtClass = models.OneToOneField(DoubtClasses, on_delete=models.PROTECT, null=True, blank=True)
isDraft = models.BooleanField(default=True)
ratings = models.FloatField(default=0)
no_of_students_attended = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'LiveClass_details'
class RegisteredNames(models.Model):
name = models.CharField(max_length=100, unique=True)
liveclass = models.ForeignKey(LiveClass_details, on_delete=Models.CASCADE)
Then, simply:
name = RegisteredNames.objects.create(name="Dhruv", liveclass_id=1)
To get all the registered names from a liveclass_details:
names = LiveClass_details.objects.get(id=1).registerednames_set.all()
num_reg = len(names)

Query a M2M related field in Django

I have a Model Return in my projects like the following:
class Kit(models.Model):
kit_name = models.CharField(max_length=500, default=0)
kit_info = models.CharField(max_length=500, default=0)
kit_client = models.ForeignKey(Client, on_delete=models.CASCADE)
class ReturnKits(models.Model):
kit = models.ForeignKey(Kit, on_delete=models.CASCADE)
quantity = models.IntegerField(default=0)
items = models.ManyToManyField(ReturnKitsProducts)
class Return(models.Model):
transaction_date = models.DateTimeField(default=datetime.now)
transaction_no = models.IntegerField(default=0, blank=True, null=True)
is_delivered = models.BooleanField(default=False)
kits = models.ManyToManyField(ReturnKits)
How can I select all the Return objects which contains the kit with kit_client = 4?
I know we can query directly related objects using __ but how to query nested ones ?
You can continue traversing the relationships with continued __ segments.
Return.objects.filter(kits__kit__kit_client=4)

Django-tables2 reverse lookup

I have a table that displays a list of "leads" which are rendered fine. There is also a related model which is called "Leadupdate" that is related to "lead" model that is used in the table. There is a many to one relationship from Leadupdate to lead with a foreign key. I want to display all the related updates for the individual "leads" in one of the updates column. There are several examples online for following forward relationship through foreign key but haven't found one for reverse yet. Here is one example of said relationship Accessor forward look up.
EDIT: Look up will be done on a Django-tables2 module instance table. I am not asking reverse look up on a model but doing it in context of Django-tables2.
Models.py:
class lead(models.Model):
slug = models.SlugField(unique=True,blank=True, null=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100, blank=True, null=True)
business_name = models.CharField(max_length=100,blank=True, null=True)
email = models.EmailField(max_length=75, blank=True, null=True)
phone_number = models.CharField(max_length=20, blank=True, null=True)
address = models.CharField(max_length=150, blank=True, null=True)
city = models.CharField(max_length=50, blank=True, null=True)
state = models.CharField(max_length=10, blank=True, null=True)
zipcode = models.CharField(max_length=5, blank=True, null=True)
submission_date = models.DateTimeField(auto_now_add=True, blank=True)
assigned_to = models.ManyToManyField(Listing,blank=True, null=True, related_name="leads")
requested_software = models.CharField(max_length=50, blank=True, null=True)
type_of_business = models.CharField(max_length=30, choices=TYPE_OF_BUSINESS, default='Bar', blank=True, null=True)
time_frame = models.CharField(max_length=10, choices=TIME_FRAME, default='1')
comments = models.TextField(blank=True, null=True)
def __unicode__(self):
return self.business_name
#models.permalink
def get_absolute_url(self):
return('listing_detail', (),{'slug' :self.slug,})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.business_name)
super(lead, self).save(*args, **kwargs)
class Leadupdate(models.Model):
CONFIDENCE_LEVEL = (
('HOT', 'HOT'),
('COLD', 'COLD'),
)
LEAD_VALUE = (
('1K3K', '1K-3K'),
('5K10K', '5K-10K'),
('10K20K', '10K-20K'),
('20K50K', '20K-50K'),
('50KUP', '5OK-UP'),
)
ESTIMATED_CLOSING = (
('1w4w', '1-4 Weeks'),
('1m3m', '1-3 Months'),
('3m6m', '3-6 Months'),
('6m+', '6+ Months'),
)
updatedate = models.DateTimeField(auto_now_add=True)
update = models.TextField(blank=True, null=True)
updatefrom = models.ForeignKey(Listing, related_name="update_from", blank=True, null=True)
lead = models.ForeignKey(lead, related_name="related_update",blank=True, null=True)
lead_confidence_level = models.CharField(max_length=10, choices=CONFIDENCE_LEVEL, default='COLD', blank=True, null=True)
estimated_lead_value = models.CharField(max_length=10, choices=LEAD_VALUE, default='1K3K', blank=True, null=True)
estimated_closing_frame = models.CharField(max_length=10, choices=ESTIMATED_CLOSING, default='1-4 Weeks', blank=True, null=True)
def __unicode__(self):
return u" %s - %s " % (self.update, self.updatedate)
Table:
class LeadTable(tables.Table):
business_name = tables.LinkColumn('lead-detail', args=[A('slug')])
updates = tables.Column(accessor='lead.related_update')
class Meta:
model = lead
fields = ("business_name","first_name", "last_name","number_of_pos","submission_date","updates")
attrs = {"class":"paleblue"}
A late answer, but here is what works for me in Django 1.8.6 with django-tables2 1.1.0 (based on Django-Tables2 Issue 156 and This answer). To access a one to many set of objects via a foreign key relation you need to just use the related_name in the accessor and then create a render method to produce what gets written to column cell. In that method you can then get all the foreign model objects and access their fields in a for loop.
class LeadTable(tables.Table):
business_name = tables.LinkColumn('lead-detail', args=[A('slug')])
updates = tables.Column(accessor='related_update')
def render_updates(self, value, table):
updates = ""
uFirst = True
updatesList = list(value.all())
for u in updatesList:
if not uFirst:
updates += ", "
else:
uFirst = False
updates += u.update
return updates
class Meta:
model = lead
fields = ("business_name","first_name", "last_name","number_of_pos","submission_date","updates")
attrs = {"class":"paleblue"}
according to django docs
in your views you can access them in this way (assuming lead_instance is an instance of lead class):
all_leadtables_for_lead = lead_instance.leadtable_set
a side note: use Capitalized names for classes (class Lead(models.Model):) in order to adhere to python PEP8 guidelines.

django queryset - need advice on queryset in view - filters and setting relationships

I am trying to generate a template/report that lists all of the listings (ads) for a customer. Most everything relates to the ID of the Listings table. A customer can have many listings, a listing will only have one listing type, and a listing can have many images. I know my view.py is messed up -- ideally I'd like to send a minimal amount of data to the template. So I would like to only send listings, images and listingtype (1, 2, or 3) data that relates to listings for a specific customer. I'm struggling with the queryset, and building the context. I'm sure I have to add more objects to the context than are currently listed.
I'm am presuming once I get the data to the template I will have to build a table row by row, and do some if/then stuff in the template to deal with the different listingtypes. Let me know if you know of an easier way.
Models.py
class Customer(models.Model):
name = models.CharField(max_length=20, blank=True)
email = models.CharField(max_length=50, blank=True)
user = models.ForeignKey(User, unique=True)
def __unicode__(self):
return unicode(self.user)
class ListingType(models.Model):
desc = models.CharField(max_length=35, blank=True)
def __unicode__(self):
return self.desc
class Listings(models.Model):
createdate = models.DateTimeField(auto_now_add=True)
price = models.IntegerField(null=True, blank=True)
listing_type = models.ForeignKey(ListingType)
customer = models.ForeignKey(Customer)
class Listingtype1(models.Model):
manufacturer = models.CharField(max_length=35, blank=True)
mfg_no = models.CharField(max_length=35, blank=True)
typespecific1 = models.CharField(max_length=35, blank=True)
typespecific2 = models.CharField(max_length=35, blank=True)
listings = models.ForeignKey(Listings)
class Listingtype2(models.Model):
manufacturer = models.CharField(max_length=35, blank=True)
mfg_no = models.CharField(max_length=35, blank=True)
typespecific1 = models.CharField(max_length=35, blank=True)
typespecific2 = models.CharField(max_length=35, blank=True)
listings = models.ForeignKey(Listings)
class Listingtype3(models.Model):
manufacturer = models.CharField(max_length=35, blank=True)
mfg_no = models.CharField(max_length=35, blank=True)
typespecific1 = models.CharField(max_length=35, blank=True)
typespecific2 = models.CharField(max_length=35, blank=True)
listings = models.ForeignKey(Listings)
class Image(models.Model):
title = models.CharField(max_length=60, blank=True, null=True)
image = models.ImageField(upload_to="images/", blank=True, null=True)
thumbnail = models.ImageField(upload_to="images/", blank=True, null=True)
listings = models.ForeignKey(Listings)
Views.py (work in progress)
def listings_customer(request, user_id):
customer = get_object_or_404(Customer, user=user_id)
cusnum=customer.id
listings = Listings.objects.filter(customer=cusnum)
image = Image.objects.all()
context=Context({
'title': 'Listings',
'customer': customer,
'listings' : listings,
'image' : image,
})
return render_to_response('bsmain/listings.html', context)
Take a look on lookups with relations and backwards relationship so you can link all your models in chains like:
Image.objects.filter(listings__customer=customer)
Also, few offtopic advices.
a listing will only have one listing type
So you should use OneToOneField here
You don't need to retrieve customer.id, use customer for lookup.
Use non-plural names for your models (Listings should be Listing) and avoid duplicating your code like in ListingType1 and ListingType2, use model inheritance