I have the following models:
# Group for Key/Value pairs
class Group(models.Model):
name = models.TextField(unique=True)
def __unicode__(self):
return self.name
class Meta:
verbose_name = 'Group'
verbose_name_plural = 'Groups'
# Key is the key/name for a Value
class Key(models.Model):
name = models.TextField(unique=True)
def __unicode__(self):
return self.name
class Meta:
verbose_name = 'Key'
verbose_name_plural = 'Keys'
# Value is the value/content of a key
class Value(models.Model):
value = models.TextField()
def __unicode__(self):
return '%s' % self.value
class Meta:
verbose_name = 'Value'
verbose_name_plural = 'Values'
class Key_Value(models.Model):
group = models.ForeignKey(Group)
key = models.ForeignKey(Key)
value = models.ForeignKey(Value)
def __unicode__(self):
return '%s = %s' % (self.key.name, self.value.value)
class Meta:
verbose_name = 'Key/Value Paar'
verbose_name_plural = 'Key/Value Paare'
Now I pass the form to the template:
def exampleview(request):
key_value_form = Key_Value_Form(request.POST)
return render_to_response(
'edit.html', {
'key_value_form': key_value_form,
})
Now lets look at possible data
KEY/VALUE PARIRS:
key = TEST 1
value = TEST 1
group = TESTGROUP 1
key = TEST 2
value = TEST 2
group = TESTGROUP 2
Now I changed the default widgets for the Key/Value Table entries to select widgets.
Here's what I want to do:
SELECT GROUP [+] [-]
--> [Now choose Key/Value pair belonging to group] [+] [-]
at the start you always get two selects one for the group and one for the key/value pair.
if you press the + at the GROUP a new group select button should appear along with a KEY/Value Pair select, if you press the + at the Key/Value select a new key/value select box should appear.
I have two problems:
ONE: I don't know how the check in the template should look like and
TWO: How I can implement those + - Buttons
Any Help is appreciated. It would be cool if this would be possible without javascript but I don't have very high hopes in that direction
You need to build the form on the fly. Suppose you had a definition of a group form built in a dictionary from a view in your view handler (containing group model foreign keys and number of actual keys in them):
# note that the dictionary d shown here is just an example
# yours should have actual group foreign keys based on Group model
d = { 'first' : 5, 'second' : 3, 'third' : 2 }
def make_groupkeys_form(descriptor):
groups = { 'select': '', 'keypairs' : [] }
fields = { 'groups' : groups }
for group, numkeys in descriptor.values():
groupchoices = # find out what choices you need to offer for group widget
groups['select'] = forms.SelectField(choices=groupchoices)
for i in range(numkeys):
keyvalchoices = # find out what choices you need to offer for keyval widget per group
groups['keypairs'].append(forms.SelectField(choices=keyvalchoices))
# now you have enough SelectFields for all your groups and keyval pairs
return type('Key_Value_Form', (forms.BaseForm,), { 'base_fields': fields })
# from view handler
def exampleview(request):
# determine from request how many groups you want and how many keyval widgets per group, for example here I will use the above predefined d
key_value_form = make_groupkeys_form(d)
return render_to_response(
'edit.html', {
'key_value_form': key_value_form,
})
Note that you can (and should) rewrite a class Key_Value_Form to inherit from forms.BaseForm and put the make_groupkeys_form(descriptor) code in its __init__(request, descriptor) member. You will also need to do write your own is_valid() to enumerate through select fields and make sure the choices are correct when the user submits the form and override clean() to attempt to validate individual user's choices.
Finally, consider going through this dynamic forms read. It will guide you step by step in how to create dynamic forms in django.
Related
I am new to django. I am working on a test project. Where I have a Model CollectFee with structure given below:
class CollectFee(models.Model):
boarder = models.ForeignKey(Boarder, on_delete=models.CASCADE)
feetype = models.ForeignKey(FeeType, on_delete=models.CASCADE)
amountdue = models.PositiveIntegerField("amount Due")
amountpaid = models.PositiveIntegerField("amount Paid")
balance = models.PositiveIntegerField("Balance")
class Meta:
verbose_name_plural = "Collect Fee"
def __str__(self):
return self.boarder.name
I want to apply a query set in the views which will display all the records where feetype_id is not 2, when this record is excluded then also exclude those records which have the same boarder_id as of already excluded record.
For example, Exclude the second row as it has feetype_id = 2 then also exclude the third row because it has the same boarder_id as of second row.
As I am new, I was able to just implement the filter below:
def feedue(request):
last_fee_type = FeeType.objects.last()
boarders = CollectFee.objects.exclude(feetype_id=last_fee_type)
context = {'boarders':boarders}
return render(request, 'fee-due.html', context)
You can exclude the many-to-one relationship with the through accessor like this:
CollectFee.objects.exclude(boarder__collectfee__feetype=last_fee_type)
This expression essentially gets the Boarder from a particular CollectFee object, then all CollectFees associated with that Boarder and their FeeTypes, allowing you to exclude accordingly.
May be this helps
def feedue(request):
excluded_objs = CollectFee.objects.filter(feetype_id=2)
border_ids = [id.border_id for obj in excluded_objs]
duefee = CollectFee.objects.exclude(boarder_id__in=border_ids)
context = {'duefee': duefee}
return render(request, 'fee-due.html', context)
I am trying to create a filter search bar that I can customize. For example, if I type a value into a search bar, then it will query a model and retrieve a list of instances that match the value. For example, here is a view:
class StudentListView(FilterView):
template_name = "leads/student_list.html"
context_object_name = "leads"
filterset_class = StudentFilter
def get_queryset(self):
return Lead.objects.all()
and here is my filters.py:
class
StudentFilter(django_filters.FilterSet):
class Meta:
model = Lead
fields = {
'first_name': ['icontains'],
'email': ['exact'],
}
Until now, I can only create a filter search bar that can provide a list of instances that match first_name or email(which are fields in the Lead model). However, this does now allow me to do more complicated tasks. Lets say I added time to the filter fields, and I would like to not only filter the Lead model with the time value I submitted, but also other Lead instances that have a time value that is near the one I submitted. Basically, I want something like the def form_valid() used in the views where I can query, calculate, and even alter the values submitted.
Moreover, if possible, I would like to create a filter field that is not necessarily an actual field in a model. Then, I would like to use the submitted value to do some calculations as I filter for the list of instances. If you have any questions, please ask me in the comments. Thank you.
You can do just about anything by defining a method on the filterset to map the user's input onto a queryset. Here's one I did earlier. Code much cut down ...
The filter coat_info_contains is defined as a CharFilter, but it is further parsed by the method which splits it into a set of substrings separated by commas. These substrings are then used to generate Q elements (OR logic) to match a model if the substring is contained in any of three model fields coating_1, coating_2 and coating_3
This filter is not implicitly connected to any particular model field. The connection is through the method= specification of the filter to the filterset's method, which can return absolutely any queryset on the model that can be programmed.
Hope I haven't cut out anything vital.
import django_filters as FD
class MemFilter( FD.FilterSet):
class Meta:
model = MyModel
# fields = [fieldname, ... ] # default filters created for these. Not required if all declarative.
# fields = { fieldname: [lookup_expr_1, ...], ...} # for specifying possibly multiple lookup expressions
fields = {
'ft':['gte','lte','exact'], 'mt':['gte','lte','exact'],
...
}
# declarative filters. Lots and lots of
...
coat_info_contains = FD.CharFilter( field_name='coating_1',
label='Coatings contain',
method='filter_coatings_contains'
)
...
def filter_coatings_contains( self, qs, name, value):
values = value.split(',')
qlist = []
for v in values:
qlist.append(
Q(coating_1__icontains = v) |
Q(coating_2__icontains = v) |
Q(coating_3__icontains = v) )
return qs.filter( *qlist )
i'm trying to call back unique constraints field , in my project i have to count number of M2M selected
class Booking(models.Model):
room_no = models.ForeignKey(Room,on_delete=models.CASCADE,blank=True,related_name='rooms')
takes_by = models.ManyToManyField(Vistor)
#property
def no_persons(self):
qnt = Booking.objects.filter(takes_by__full_information=self).count()#but this doesnt work
return qnt
Cannot query "some room information": Must be "Vistor" instance.
class Vistor(models.Model):
full_name = models.CharField(max_length=150)
dob = models.DateField(max_length=14)
city = models.ForeignKey(City,on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=['full_name','dob','city'],name='full_information')
]
def __str__(self):
return f'{self.full_name} - {self.city} - {self.dob}'
it it possible to access full_information through Booking model ? thank you ..
If you want to count the number of Visitors related to that booking, you can count these with:
#property
def no_persons(self):
self.taken_by.count()
This will make an extra query to the database, therefore it is often better to let the database count these in the query. You can thus remove the property, and query with:
from django.db.models import Count
Booking.objects.annotate(
no_persons=Count('takes_by')
)
The Bookings that arise from this QuerySet will have an extra attribute no_persons with the number of related Visitors.
Still learning Django, so not sure if there's a nice way to do this.
I have a few models with specific attributes (all use Item as base class), and a metadata table (id, language, type, value) used to store any extra attributes that could be potentially associated with instances of any of those models (code below). These models are used with a form / template, simple web-based CRUD.
Right now, I call .save_metadata(...) and .load_metadata(...) explicitly, and use .form_initia(...) to populate the form with metadata that isn't explicitly in the model.
I'm looking for a way to handle this automatically -- basically implementing a model with a variable number of fields, key ones are columns in the model's table, the other ones are rows in the metadata table, and are instance-specific. Is there a way of hooking a method after objects.get(...) or objects.filter(...) etc? I've messed with custom managers and looked into signals, but nothing seems to lead towards an acceptable solution.
class Item(models.Model):
mdata = ['title'] # metadata associated with item
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add = True)
status = models.IntegerField(default=0, choices = ([(0,'Staged'), (1,'Published'),(2,'Archived'), ]))
def set_status(self, s):
self.status = s
self.save()
# stores metadata attributes associated with current item
def save_metadata(self, lang, form):
for mt in self.mdata:
try:
md = Metadata.objects.get(item=self, lang=lang, t=mt)
except Metadata.DoesNotExist:
md = Metadata.objects.create(item=self, lang=lang, t=mt)
md.v=form.cleaned_data[mt]
md.save()
# retrieves metadata attributes associated with current item
def load_metadata(self, lang):
for mt in self.mdata:
self.__dict__[mt] = None
try:
self.__dict__[mt] = Metadata.objects.get(item=self, t=mt, lang=lang).v
except Metadata.DoesNotExist:
md = Metadata.objects.filter(item=self, t=mt)
if len(md) > 0:
self.__dict__[mt] = md[0].v
# provides metadata attributes associated with current item needed to populate a form
def form_initial(self, seed=None):
meta = {}
for mt in self.mdata:
meta[mt] = self.__dict__[mt]
#meta[mt] = 'test'
if seed:
meta = dict(meta.items() + seed.items())
return meta
# used to store various metadata associated with models derived from Item
class Metadata(models.Model):
item = models.ForeignKey(Item)
lang = models.CharField(max_length = 8)
t = models.CharField(max_length = 250)
v = models.CharField(max_length = 2500)
EDIT:
It turns out the real question is - how do I get select_related to follow the m2m relationships I have defined? Those are the ones that are taxing my system. Any ideas?
I have two classes for my django app. The first (Item class) describes an item along with some functions that return information about the item. The second class (Itemlist class) takes a list of these items and then does some processing on them to return different values. The problem I'm having is that returning a list of items from Itemlist is taking a ton of queries, and I'm not sure where they're coming from.
class Item(models.Model):
# for archiving purposes
archive_id = models.IntegerField()
users = models.ManyToManyField(User, through='User_item_rel',
related_name='users_set')
# for many to one relationship (tags)
tag = models.ForeignKey(Tag)
sub_tag = models.CharField(default='',max_length=40)
name = models.CharField(max_length=40)
purch_date = models.DateField(default=datetime.datetime.now())
date_edited = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
buyer = models.ManyToManyField(User, through='Buyer_item_rel',
related_name='buyers_set')
comments = models.CharField(default='',max_length=400)
house_id = models.IntegerField()
class Meta:
ordering = ['-purch_date']
def shortDisplayBuyers(self):
if len(self.buyer_item_rel_set.all()) != 1:
return "multiple buyers"
else:
return self.buyer_item_rel_set.all()[0].buyer.name
def listBuyers(self):
return self.buyer_item_rel_set.all()
def listUsers(self):
return self.user_item_rel_set.all()
def tag_name(self):
return self.tag
def sub_tag_name(self):
return self.sub_tag
def __unicode__(self):
return self.name
and the second class:
class Item_list:
def __init__(self, list = None, house_id = None, user_id = None,
archive_id = None, houseMode = 0):
self.list = list
self.house_id = house_id
self.uid = int(user_id)
self.archive_id = archive_id
self.gen_balancing_transactions()
self.houseMode = houseMode
def ret_list(self):
return self.list
So after I construct Itemlist with a large list of items, Itemlist.ret_list() takes up to 800 queries for 25 items. What can I do to fix this?
Try using select_related
As per a question I asked here
Dan is right in telling you to use select_related.
select_related can be read about here.
What it does is return in the same query data for the main object in your queryset and the model or fields specified in the select_related clause.
So, instead of a query like:
select * from item
followed by several queries like this every time you access one of the item_list objects:
select * from item_list where item_id = <one of the items for the query above>
the ORM will generate a query like:
select item.*, item_list.*
from item a join item_list b
where item a.id = b.item_id
In other words: it will hit the database once for all the data.
You probably want to use prefetch_related
Works similarly to select_related, but can deal with relations selected_related cannot. The join happens in python, but I've found it to be more efficient for this kind of work than the large # of queries.
Related reading on the subject