Override ChoiceField choice attribute with for loop in Django views - python

I m trying to override ChoiceField in forms in which i can loop through specific object in my views,
But i Failed cause i only get in the template form only the last item in the list..
need some help to get all the choices i need from this object.
models.py
class TourPackageBuyer(models.Model):
tour = models.ForeignKey(TourPackage, on_delete=models.CASCADE, null =True) production
number_choice = [(i,i) for i in range(6)]
number_choice_2 = [(i,i) for i in range(18)]
number_choice_3 = [(i,i) for i in range(60)]
user = models.CharField(settings.AUTH_USER_MODEL, max_length=200)
num_of_adults = models.PositiveIntegerField(default=0, choices= number_choice_2, null=True)
num_of_children = models.PositiveIntegerField(default=0, choices= number_choice_3, null=True)
hotel = models.ManyToManyField(PackageHotel, blank=True)### thats the field
forms.py
class TourPackageBuyerForm(ModelForm):
class Meta:
model = TourPackageBuyer
date = datetime.date.today().strftime('%Y')
intDate = int(date)
limitDate = intDate + 1
YEARS= [x for x in range(intDate,limitDate)]
# YEARS= [2020,2021]
Months = '1',
# fields = '__all__'
exclude = ('user','tour','invoice','fees', 'paid_case')
widgets = {
'pickup_date': SelectDateWidget(empty_label=("Choose Year", "Choose Month", "Choose Day")),
'hotel': Select(),
# 'pickup_date': forms.DateField.now(),
}
hotel = forms.ChoiceField(choices=[]) ### Thats the field i m trying to override
views.py
def TourPackageBuyerView(request, tour_id):
user = request.user
tour = TourPackage.objects.get(id=tour_id)
tour_title = tour.tour_title
hotels = tour.hotel.all()
form = TourPackageBuyerForm(request.POST or None, request.FILES or None)
### im looping through specific items in the model in many to many field
for h in hotels:
form.fields['hotel'].choices = (h.hotel, h.hotel), ### when this loop it just give the last item in the form in my template!!

You are reassigning the value of choices every time through the loop, so you'll only get the last value you assign once the loop is finished.
You can fix this by replacing this:
for h in hotels:
form.fields['hotel'].choices = (h.hotel, h.hotel),
With this list comprehension:
form.fields['hotel'].choices = [(h.hotel, h.hotel) for h in hotels]
or if you want a tuple as output you can do:
form.fields['hotel'].choices = tuple((h.hotel, h.hotel) for h in hotels)

Related

How to insert ManyToMany field in django

I want to insert a ManyToMany fields in my db using django.I select some customers using checkboxes.
This is my models.py :
class Campaign(models.Model):
title = models.CharField(max_length=255)
channel = models.CharField(max_length=255)
start_date = models.DateField()
end_date = models.DateField()
target_prospect = models.ManyToManyField(ProspectClient,related_name='campaigns_prospect')
target_partner = models.ManyToManyField(PartnerClient,related_name='campaigns_partners')
I try the code below in my views.py but didn't work :
def campaigns_page(request):
if request.user.is_authenticated:
if request.user.profile == 'D' or request.user.profile == 'E' or request.user.is_superuser:
campaigns = Campaign.objects.all()
prospects = ProspectClient.objects.all()
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
context = {
'campaigns':campaigns,
'prospects':prospects,
'partners':partners
}
if request.method == 'POST':
title = request.POST['title']
channel = request.POST['channel']
start_date = request.POST['start_date']
end_date = request.POST['end_date']
descriptions = request.POST['goals'].split(",")
targets = request.POST['targets']
campaign = Campaign.objects.create(title=title,channel=channel,start_date=start_date,end_date=end_date)
for description in descriptions:
goal = Goal.objects.create(description=description)
goal.campaign.add(campaign)
for target in targets:
prospects.campaign.add(campaign)
partners.campaign.add(campaign)
return render(request,'CampaignManagement/campaigns_page.html',context)
return render(request, 'Login/logout.html')
If I delete the part of tergets it works.
But with this part it gives me This error : 'QuerySet' object has no attribute 'campaign'
How I can solve this ?
I see a couple of errors. Perhaps one or more are leading to the problem.
One
Try printing this:
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
print(partners)
I suspect it will print None since you are excluding all id's in PartnerClient.objects.values('id'). On another note you don't need the all() since exclude() will return all the results you are looking for.
Two
In the line for target in targets: what exactly are you iterating through? targets = request.POST['targets'] is just giving you a string, so it would iterate through each letter. Perhaps you meant:
targets = request.POST['targets'].split(", ")
like you did for descriptions? Or perhaps you are getting a list of items from your form, in which case you can use:
targets = request.POST.getlist('targets')

Django : Best way to Query a M2M Field , and count occurences

class Edge(BaseInfo):
source = models.ForeignKey('Node', on_delete=models.CASCADE,related_name="is_source")
target = models.ForeignKey('Node', on_delete=models.CASCADE,related_name="is_target")
def __str__(self):
return '%s' % (self.label)
class Meta:
unique_together = ('source','target','label','notes')
class Node(BaseInfo):
item_type_list = [('profile','Profile'),
('page','Page'),
('group','Group'),
('post','Post'),
('phone','Phone'),
('website','Website'),
('email','Email'),
('varia','Varia')
]
item_type = models.CharField(max_length=200,choices=item_type_list,blank = True,null=True)
firstname = models.CharField(max_length=200,blank = True, null=True)
lastname = models.CharField(max_length=200,blank = True,null=True)
identified = models.BooleanField(blank=True,null=True,default=False)
username = models.CharField(max_length=200, blank=True, null=True)
uid = models.CharField(max_length=200,blank=True,null=True)
url = models.CharField(max_length=2000,blank=True,null=True)
edges = models.ManyToManyField('self', through='Edge',blank = True)
I have a Model Node (in this case a soc media profile - item_type) that has relations with other nodes (in this case a post). A profile can be the author of a post. An other profile can like or comment that post.
Question : what is the most efficient way to get all the distinct profiles that liked or commented on anothes profile's post + the count of these likes /comments.
print(Edge.objects.filter(Q(label="Liked")|Q(label="Commented"),q).values("source").annotate(c=Count('source')))
Gets me somewhere but i have the values then (id) and i want to pass the objects to my template rather then .get() all the profiles again...
Result :
Thanks in advance
I ended up with iterating over the queryset and adding the objects that i wanted in a dictionary , if the object was already in dictionary , i would count +1 and add the relation in a nested list.
This doesnt feel right but works for now.
posts = Edge.objects.filter(source = self,target__item_type='post',label='Author')
if posts:
q = Q()
for post in posts:
q = q | Q(target=post.target)
contributors = Edge.objects.filter(Q(label="Liked")|Q(label="Commented"),q)
if contributors:
for i in contributors:
if i.source.uid in results:
if i.label in results[i.source.uid]['relation']:
pass
else:
results[i.source.uid]["relation"].append(i.label)
if 'post' in results[i.source.uid]:
results[i.source.uid]['post'].append(i.target)
else:
results[i.source.uid]['post']=[i.target]
else:
results[i.source.uid] = {'profile' : i.source , 'relation':[i.label],'post':[i.target]}

How can I create a ManyToMany relationship with the same model in Django and save to database?

I have a form which is collecting data about a variable to be created. I want to create a list of variables which are already there in the database. I am doing this by creating a ManyToMany relationship. When I start the server, the list of variables gets saved on the application, but it does not alter the database field named selective list.
forms.py
class VariableForm(ModelForm):
class Meta:
model = variable
fields = ['name', 'area', 'parameterName', 'order', 'type', 'format', 'units', 'comboItems',
'hiAlarm', 'loAlarm', 'scaleHiMax', 'scaleLoMax', 'deviationAlarm','selectiveList', 'round',
'days', 'required', 'hidden', 'readOnly', 'holdLast', 'calibrationFrequency',
'dateNextCalibration', 'triggerCalibrated']
widgets = {
'comboItems': forms.Textarea(attrs={'rows':1, 'cols': 40, 'style': 'height: 2em;padding-top:0'}),
'forceValue': forms.Textarea(attrs={'rows':1, 'cols': 40, 'style': 'height: 2em;padding-top:0',
'placeholder':'This will force all input to this variable'})
}
#selectiveList = forms.ModelMultipleChoiceField(queryset=variable.objects.all().order_by('-name').reverse())
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(VariableForm, self).__init__(*args, **kwargs)
self.fields['round'] = forms.ModelMultipleChoiceField(
queryset=opRound.objects.all(),
widget=forms.SelectMultiple,
label='Rounds',
required=False
)
self.fields['selectiveList'] = forms.ModelMultipleChoiceField(
queryset=variable.objects.all().order_by('-name').reverse(),
widget=forms.SelectMultiple,
label='Select Variables',
required=False
)
self.fields['days'] = forms.ModelMultipleChoiceField(
queryset=dayOfWeek.objects.all(),
widget=forms.SelectMultiple,
label='Days',
required=False
)
self.fields['area'].choices = AreaIterator(request = self.request)
try:
self.fields['order'].initial = variable.objects.latest().order+1
except Exception,e:
print e
model.py
class variable(models.Model):
name = models.CharField(max_length=255)
area = models.ForeignKey(area)#parent
order = models.IntegerField("Order Index (0 is first, 1 is next, etc.)",default=999)#order index to display in order correctly, ascending
type = models.CharField(max_length=255, choices=(("Value", "Value"), ("Runtime", "Runtime/FlowTotal"),
("Message", "Message"), ("CheckBox", "Check Box List"), ("Selection", "Selection Box"), ("Formula2", "Formula with other Variables"),
("OnOff", "In/Out of Service Selection"), ("OnOffSelection", "Selective On/Off")),
default = "Value" )#what type of variable
format = models.CharField(max_length=255,choices=(("Number", "Number (Without Decimals)"),
("2Number", "Number (With Decimals)"), ("Date", "Date"),
("Time", "Time"), ("Text", "Text")), blank=True, null=True, default="2Number" )#number format if needed
units = models.CharField(max_length=255,blank=True,null=True)#units of measurement
required = models.BooleanField(default=True)#is the variable required in a round
hiAlarm = models.FloatField("High Alarm",blank=True,null=True)#red notify if above
loAlarm = models.FloatField("Low Alarm",blank=True,null=True)#yellow notify if below
scaleHiMax = models.FloatField("Limit maximum value",blank=True,null=True)#scale to high max if needed
scaleLoMax = models.FloatField("Limit low value",blank=True,null=True)#scale to low if needed
deviationAlarm = models.FloatField("Deviation Alarm",blank=True,null=True,
help_text="Triggers an alarm if the value between this record and the last is greater than this percentage.")#%change check
round = models.ManyToManyField(opRound,blank=True)#round of gathering data
days = models.ManyToManyField(dayOfWeek,blank=True)#day of the week
selectiveList = models.ManyToManyField("self",through="variable",blank=True,symmetrical=False)#List to determine which variables to display when the selection is "OFF"
parameterName = models.CharField("Parameter Name (ReportID)",max_length=255,blank=True,null=True)#hachWIM ID
comboItems = models.TextField("List of comma separated options.", blank=True,null=True)#list deliminated by a column for choosing
#PUT EQUATION HERE
hidden = models.BooleanField("Sync to tablet",default=True)#this muse be True if required is true
readOnly = models.BooleanField("Read only.", default = False)
dateTimeEdited = models.DateTimeField(auto_now=True)#date edited
userEdited = models.ForeignKey(CustomUser,blank=True,null=True,related_name="userEditedV")# last user to edit data
forceValue = models.TextField(blank=True,null=True)#force reading
userForced = models.ForeignKey(CustomUser,blank=True,null=True,related_name="userForcedV")# last user to force data
useForce = models.BooleanField("Turn Forcing On", default=False)
dateNextCalibration = models.DateField("Next Calibration Date", blank=True,null=True, help_text="YYYY-MM-DD")
triggerCalibrated = models.BooleanField("Trigger next calibration date", default=False)
calibrationFrequency = models.ForeignKey(calibrationFrequency, blank=True, null=True)
version = models.BigIntegerField(default=1)#used for sync
holdLast = models.BooleanField("Selecting this will hold the last value on the tablet automatically.", default=False)
When we are trying to do it from different models, such as round or days, it creates a new database table with those relations. I want to store the selected values as a string list in the selective list column in the same model.
Here is what the multiple select looks like.

How do I display Django data from a related model of a related model?

I am trying to display data from several models that are related together through a QuerySet. My ultimate goal is to display some information from the Site model, and some information from the Ppack model, based on a date range filter of the sw_delivery_date in the Site model.
Here are my models:
class Site(models.Model):
mnemonic = models.CharField(max_length = 5)
site_name = models.CharField(max_length = 100)
assigned_tech = models.ForeignKey('Person', on_delete=models.CASCADE, null = True, blank = True)
hw_handoff_date = models.DateField(null = True, blank = True)
sw_delivery_date = models.DateField(null = True, blank = True)
go_live_date = models.DateField(null = True, blank = True)
web_url = models.CharField(max_length = 100, null = True, blank = True)
idp_url = models.CharField(max_length = 100, null = True, blank = True)
def __str__(self):
return '(' + self.mnemonic + ') ' + self.site_name
class Ring(models.Model):
ring = models.IntegerField()
def __str__(self):
return "6." + str(self.ring)
class Ppack(models.Model):
ppack = models.IntegerField()
ring = models.ForeignKey('Ring', on_delete=models.CASCADE)
def __str__(self):
return str(self.ring) + " pp" + str(self.ppack)
class Code_Release(models.Model):
Inhouse = 'I'
Test = 'T'
Live = 'L'
Ring_Location_Choices = (
(Inhouse, 'Inhouse'),
(Test, 'Test'),
(Live, 'Live'),
)
site_id = models.ForeignKey('Site', on_delete=models.CASCADE)
type = models.CharField(max_length = 1, choices = Ring_Location_Choices, blank = True, null = True)
release = models.ForeignKey('Ppack', on_delete=models.CASCADE)
def __str__(self):
return "site:" + str(self.site_id) + ", " + self.type + " = " + str(self.release)
If I use the following,
today = datetime.date.today()
future = datetime.timedelta(days=60)
new_deliveries = Site.objects.select_related().filter(sw_delivery_date__range=[today, (today + future)])
I can get all of the objects in the Site model that meet my criteria, however, because there is no relation from Site to Code_Release (there's a one-to-many coming the other way), I can't get at the Code_Release data.
If I run a for loop, I can iterate through every Site returned from the above query, and select the data from the Code_Release model, which allows me to get the related data from the Ppack and Ring models.
site_itl = {}
itl = {}
for delivery in new_deliveries:
releases = Code_Release.objects.select_related().filter(site_id = delivery.id)
for rel in releases:
itl[rel.id] = rel.release
site_itl[delivery.id] = itl
But, that seems overly complex to me, with multiple database hits and possibly a difficult time parsing through that in the template.
Based on that, I was thinking that I needed to select from the Code_Release model. That relates back to both the Site model and the Ppack model (which relates to the Ring model). I've struggled to make the right query / access the data in this way that accomplishes what I want, but I feel this is the right way to go.
How would I best accomplish this?
You can use RelatedManager here. When you declare ForeignKey, Django allows you to access reverse relationship. To be specific, let's say that you have multiple code releases that are pointing to one specific site. You can access them all via site object by using <your_model_name_lowercase>_set attribute. So in your case:
site.code_release_set.all()
will return QuerySet of all code release objects that have ForeignKey to object site
You can access the Releases from a Site object. First, you can put a related_name to have a friendly name of the reverse relation between the models:
site_id = models.ForeignKey('Site', on_delete=models.CASCADE, related_name="releases")
and then, from a Site object you can make normal queries to Release model:
site.releases.all()
site.releases.filter(...)
...

Auto increament the invoice number in django backend for new invoice

I want to auto increament the invoice number which is 3 digits char and 4 digits number.
class Invoice:
invoice_no = models.CharField(max_length=500, null=True, blank=True, validators=[RegexValidator(regex='^[a-zA-Z0-9]*$',message='Invoice must be Alphanumeric',code='invalid_invoice number'),])
I register this model in backend. But now when i click on create invoice in admin the invoice should be auto filled. When i again click on create new invoice in admin, the invoice_number should be incremented by one and should be auto field.
Ex for Invoice number MAG0001, MAG0002, MAG0003 etc and this should be auto field in admin when i click on create new invoice.
Define a function to generate invoice number.
def increment_invoice_number():
last_invoice = Invoice.objects.all().order_by('id').last()
if not last_invoice:
return 'MAG0001'
invoice_no = last_invoice.invoice_no
invoice_int = int(invoice_no.split('MAG')[-1])
new_invoice_int = invoice_int + 1
new_invoice_no = 'MAG' + str(new_invoice_int)
return new_invoice_no
Now use this function as default value in your model filed.
invoice_no = models.CharField(max_length=500, default=increment_invoice_number, null=True, blank=True)
This is just an idea. Modify the function to match your preferred invoice number format.
In above arulmr answer just edit char field
def increment_invoice_number():
last_invoice = Invoice.objects.all().order_by('id').last()
if not last_invoice:
return 'MAG0001'
invoice_no = last_invoice.invoice_no
invoice_int = int(invoice_no.split('MAG')[-1])
width = 4
new_invoice_int = invoice_int + 1
formatted = (width - len(str(new_invoice_int))) * "0" + str(new_invoice_int)
new_invoice_no = 'MAG' + str(formatted)
return new_invoice_no
class Invoice(models.Model):
invoice_no = models.CharField(max_length = 500, default = increment_invoice_number, null = True, blank = True)
This will work fine.
def invoiceIncrement():
get_last_invoice_number
incremente_last_invoice_number
return next_invoice_number
class Invoice:
invoice_no = models.CharField(max_length=500, null=True, blank=True,
validators=[RegexValidator(regex='^[a-zA-Z0-9]*$',
message='Invoice must be Alphanumeric',code='invalid_invoice number'),],
default=invoiceIncrement)
Try this: there are some obvious issues:
if more than one person adds an invoice at the same time, could have collision
will need to make an extra db call each time you create a new invoice.
Also: you may want to just consider using either an auto_increment or UUID.
Maybe this code can help
def increment_invoice_number():
last_invoice = Invoice.objects.all().order_by('id').last()
if not last_invoice:
return 'MAG0001'
invoice_no = last_invoice.invoice_no
new_invoice_no = str(int(invoice_no[4:]) + 1)
new_invoice_no = invoice_no[0:-(len(new_invoice_no))] + new_invoice_no
return new_invoice_no
def invoice_number_gen():
last_invoice = Invoice.objects.all().order_by('id').last()
last_invoice_number = last_invoice.invoice_no
#invoice number format is 'customer_name_short + number' eg: CS003
last_invoice_digits =int(last_invoice_number[2:])
#comment: slicing CS003 to get the number 003 and converting to int.
last_invoice_initials = last_invoice_number[:2]
new_invoice_digits = last_invoice_digits + 1
new_invoice_number = last_invoice_initials + str(new_invoice_digits)
return (new_invoice_number)

Categories

Resources