IS there a method to make Django check for missing id number and create a new item in this slot, instead of making it with a new id.
Here is what am trying to do
I have the model :
class BuyInvoice(models.Model):
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=PROTECT)
branch = models.ForeignKey(Branch, on_delete=PROTECT)
supplier = models.ForeignKey(Supplier, on_delete=PROTECT)
total = models.PositiveIntegerField(default=0)
is_done = models.BooleanField(default=False)
is_canceled = models.BooleanField(default=False)
def __str__(self):
return 'فاتورة بيع رقم ' + str(self.pk)
whenever I add a new item to it it takes the auto-generated id as 1, 2, 3, 4, 5
now if I deleted the item with the id 3, then I try to create new item I want it to be added to the id of 3 instead of 6
I did it manually by looping over items and find a gap to fill :
check_for_gap = BuyInvoice.objects.all()
number = 1
for item in check_for_gap:
if item.id != number:
new_invoice = BuyInvoice.objects.create(user=user, branch=branch, supplier=supplier_obj, id=number)
return redirect('invoice_buy_details', pk=new_invoice.id)
else:
number += 1
Related
I am building a recommendation engine for a tv app and end with empty lists in the results from deleted shows in the db. I'm aware that sqlite doesn't reuse id's once they are delete, i.e. 1,2,3 -> delete 2 - new data starts at 4.
The problem is that my loop is parsing the deleted ids and matching them as a top result as if they are still holding on to old attributes.
2 [<Show: Almost Human>]
3 [<Show: As We See It>]
4 [<Show: Atlanta>]
5 []
6 [<Show: Billions>]
7 [<Show: And Then There Were None>]
8 [<Show: Arrow>]
9 [<Show: Berlin Station>]
10 [<Show: Black Lightning>]
11 [<Show: black-ish>]
12 []
13 []
14 [<Show: The Black Donnellys>]
15 [<Show: Alex, Inc.>]
How do I test for deleted/null/undefined ids and bypass them within the logic of the for loop?
Here is the function from views.py:
# this is specific to the certain show page
show_obj = Show.objects.get(id=id)
...
# fetches all the show objects that are displayed as self.title
recom_show_data = []
fetch = Show.objects.all()
# loops through each show object
for show_title in fetch:
#grabs all genre tags that belong to each show object with a manytomany class model named Tag
genre_data = show_title.tag.all()
# grabs all element ids
show_title_id = show_title.id
recom_show_data.append((show_title, genre_data, show_title_id))
#pprint(recom_show_data)
df = pd.DataFrame(recom_show_data, columns = ['Title', 'Genre', 'Show_id'])
# creates a column to hold the combined strings
df['important_features'] = df['Title'].astype(str) + df['Genre'].astype(str)
#pprint(df)
# convert the text to a matrix of token counts
cm = CountVectorizer().fit_transform(df['important_features'])
# get the cosine similarity matrix from the count matrix
cs = cosine_similarity(cm)
# Get the title from show_obj = Show.objects.get(id=id)
matched_title_id = df[df.Title == show_obj]['Show_id'].values[0]
# Creates a list of enumerations for the similarity score [ (matched_title_id, similarity score), (...) ]
scores = list(enumerate(cs[matched_title_id]))
# sorts the list
sorted_scores = sorted(scores, key= lambda x:x[1], reverse = True)
# sorts scores at position 1 and up excluding position 0 which is the original title show
sorted_scores = sorted_scores[1:]
print(sorted_scores)
# Create a loop to print first 15 similar movies
j = 0
print('The 15 most recommended movies to', show_obj, 'are:\n')
for item in sorted_scores:
show_title = df[df.Show_id == item[0]]['Title'].values
print(j+1, show_title)
j = j+1
if j>14:
break
This is the models.py
class Show(models.Model):
title = models.CharField(max_length=200)
#description = models.TextField()
count = models.IntegerField()
#year = models.IntegerField()
script = models.FileField(blank=True, null=True, upload_to="screenplays")
#poster = models.ImageField(blank=True, null=True, default='default.jpg', upload_to="posters")
creators = models.ManyToManyField(Creator, related_name='shows')
tag = models.ManyToManyField(Tag, related_name='tags')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
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]}
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)
I want to create an error message for following form:
class ExaminationCreateForm(forms.ModelForm):
class Meta:
model = Examination
fields = ['patient', 'number_of_examination', 'date_of_examination']
Models:
class Patient(models.Model):
patientID = models.CharField(max_length=200, unique=True, help_text='Insert PatientID')
birth_date = models.DateField(auto_now=False, auto_now_add=False, help_text='YYYY-MM-DD')
gender = models.CharField(max_length=200,choices=Gender_Choice, default='UNDEFINED')
class Examination(models.Model):
number_of_examination = models.IntegerField(choices=EXA_Choices)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
date_of_examination = models.DateField(auto_now=False, auto_now_add=False, help_text='YYYY-MM-DD')
Every Patient has 2 Examinations (number of examination = Choices 1 or 2) and the error message should be activated when the date of the second examination < date of the first examination. Something like this:
Solution: `
def clean_date_of_examination(self):
new_exam = self.cleaned_data.get('date_of_examination')
try:
old_exam = Examination.objects.get(patient=self.cleaned_data.get('patient'))
except Examination.DoesNotExist:
return new_exam
if old_exam:
if old_exam.date_of_examination > new_exam:
raise forms.ValidationError("Second examination should take place after first examination")
return new_exam`
def clean_date_of_examination(self):
new_exam = self.cleaned_data.get('date_of_examination')
old_exam = Examination.objects.get(patient = self.cleaned_data.get('Patient'))
if old_exam:
if old_exam.date_of_examination > new_exam.date_of_examination:
raise forms.ValidationError("Second examination should take place after first examination")
return data
def clean_date_of_examination(self):
# Where 'data' is used?
date_of_exam = self.cleaned_data['date_of_examination']
try:
pat1 = Patient.object.get(examination__number_of_examination=1, date_of_examination=date_of_exam)
except Patiens.DoesNotExist:
# Patient 1 with given query doesn't exist. Handle it!
try:
pat2 = Patient.object.get(examination__number_of_examination=2, date_of_examination=date_of_exam)
except Patiens.DoesNotExist:
# Patient 2 with given query doesn't exist. Handle it!
if pat2.date_of_examination < pat1.date_of_examination:
raise forms.ValidationError("Second examination should take place after first examination")`
return data`
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)