In my django app I am creating Barcodes with a combination of str and id of model and id of product.
The barcode is generated but the problem that I am encountering is when I scan the barcode I want to show the information of the product scanned.
I'll be able to understand the problem with code in a better way
Models.py
class GrnItems(models.Model):
item = models.ForeignKey(Product, on_delete=models.CASCADE)
item_quantity = models.IntegerField(default=0)
item_price = models.IntegerField(default=0)
label_name = models.CharField(max_length=25, blank=True, null=True)
class Grn(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
reference_no = models.CharField(max_length=500, default=0)
items = models.ManyToManyField(GrnItems)
Views.py
def PrintGRNsv1(request, pk):
grn = Grn.objects.filter(pk=pk)[0]
grn_prod = grn.items.all()
print("grn prod", grn_prod)
items = []
for i in grn_prod:
for j in range(i.item_quantity):
items.append({'bar': "YNT9299" + str(pk) +
str(i.item.pk) + str(j + 1)} )
Now let's suppose I generated a Barcode YNT92991231, Now I have no idea how to get the i.item.pk from this code
How can I do this ?
P.s.
Okay the cherry on top is that I have created the Barcodes for a large number of products and they are already placed on them, so can't really change the barcode format at this point
For now when we can't change anything. Generate all the barcodes for you GRN. Save them in files or in DB. Whenever there is a query that matches those just find them from there. If there is any conflict (when 2 or more product has the same barcode) Django USER can be one way to resolve the conflict.
For new barcodes change the generator function. Use delimiter or fixed-width characters (for pk).
ex: YNT9299GGGGGPPPPPCCCCC where G => GRN pk, P => Product pk, C => Count. Or with delimiters YNT9299G1P23C2.
Yep, the solution is not a foolproof solution is just a workaround for the mess created.
Related
I'm trying to create a twitter clone and this is my user and tweet Model(some irrelevant fields have been removed).
class TwitterUser(models.Model):
user = models.OneToOneField(to=User, on_delete=models.CASCADE,primary_key=True)
Bio = models.CharField(max_length=200, blank=True)
Location = models.CharField(max_length=200, blank=True)
Website = models.URLField(blank=True)
ProfilePicture = models.ImageField(upload_to="Twitter", default="../static/twitter/images/default_profile.png")
CreateDate = models.DateField(default=timezone.now)
class Tweet(models.Model):
TweetBody = models.CharField(max_length=140, blank=False)
TweetDate = models.DateTimeField(default=timezone.now)
Owner= models.ForeignKey(to=TwitterUser,on_delete=models.CASCADE,related_name="Owner")
RetweetedBy= models.ManyToManyField(to=TwitterUser,related_name="Retweeted",blank=True,through="RetweetIntermediate")
and this the table that my many to many relationship for retweet is using.
class RetweetIntermediate(models.Model):
twitteruser=models.ForeignKey(TwitterUser,on_delete=models.CASCADE)
tweet=models.ForeignKey(Tweet,on_delete=models.CASCADE)
retweetDate=models.DateTimeField(default=timezone.now)
In profile view all the tweets and retweets should be shown ordered by date
what I'm doing right now (and it is working fine) is this:
def keymaker(a):
return a.TweetDate
def ProfileView(request):
tweets= list(Tweet.objects.filter(Owner=user.user_id,IsReplyToTweet__isnull=True).order_by("-TweetDate"))
retweets = list(user.Retweeted.all().order_by("-id"))
retweetInter=RetweetIntermediate.objects.all().order_by("-tweet_id")
for i , j in zip(retweets,retweetInter):
i.TweetDate=j.retweetDate
tweets=(tweets+retweets)
tweets.sort(key=keymaker,reverse=True)
I retrieve all the tweets ordered by date. then I retrieve all of retweets and make a list out of them and change the data of tweet to the date saved in intermediate table
and merge both lists and sort them by date.
I want to know is there a better way or more standard way to do this?
Thanks in advance.
You can do it using union together with annotate.
from django.db.models import F
tweets_qs = Tweet.objects\
.filter(Owner=user, IsReplyToTweet__isnull=True)\
.annotate(date=F('TweetDate'))
retweets_qs = Tweet.objects\
.filter(retweetintermediate__twitteruser=user)\
.annotate(date=F('retweetintermediate__retweetDate'))
timeline_qs = tweets_qs.union(retweets_qs).order_by('-date')
Notice that both querysets have Tweet objects.
Edit: Sorry for not understanding the question correctly the first time.
I came up with a solution but it's using a model method ( as far as i understand it cannot be used with X.objects.filter() ) and then use the python method sorted. I've read that it's way faster to use django ORM than direct python so I'm searching for a solution. I precise that adding fields to my model is not possible as the database is already well populated.
Basically I've an Articles model :
class Articles(models.Model):
title = models.CharField(max_length=200, null=False, blank=False)
image = models.URLField(null=False, blank=False)
summary = models.TextField()
link = models.URLField(null=False, blank=False)
pub_date = models.DateTimeField(default=timezone.now)
source = models.ForeignKey(
Sources, on_delete=models.CASCADE, related_name="souurce")
category = models.ManyToManyField(Categories)
and what I want to do is ordering result by approximate publication date ( for example an article published at 5:34 and an another one published a 5:31 are both considered published at the same time ), then I can perform other orderings like per category, source or even by title.
Here is my class method to do that (by closest 10 minutes ):
def get_approximate_date(self):
pub_date = self.pub_date
def timeround10(dt):
"""timertound closets 10 minutes"""
a, b = divmod(round(dt.minute, -1), 60)
h = (dt.hour + a) % 24
m = b
new = dt.replace(hour=h, minute=m, second=0, microsecond=0)
return new
return timeround10(pub_date)
Then in my view I can do the following ( I chose to order by approximate date then by reverse alphabetical order ) :
articles_ = Articles.objects.all()
articles_list = sorted(articles_, key=lambda i: (i.get_approximate_date(), i.summary), reverse=True)
The closest thing I came up with using only django ORM is :
Articles.objects.order_by("-pub_date__year","-pub_date__month","-pub_date__day","-summary")
Apart from being ugly it only round the pub date by hour, so 1:59 PM = 1:01PM.
I'm aware of Trunc https://docs.djangoproject.com/en/3.1/ref/models/database-functions/#trunc but it doesn't only implement a way to order by hour minutes etc, maybe i should expand it if it's the only option.
Thanks in advance !
After solving the problem I asked about in this question, I am trying to optimize performance of the FTS using indexes.
I issued on my db the command:
CREATE INDEX my_table_idx ON my_table USING gin(to_tsvector('italian', very_important_field), to_tsvector('italian', also_important_field), to_tsvector('italian', not_so_important_field), to_tsvector('italian', not_important_field), to_tsvector('italian', tags));
Then I edited my model's Meta class as follows:
class MyEntry(models.Model):
very_important_field = models.TextField(blank=True, null=True)
also_important_field = models.TextField(blank=True, null=True)
not_so_important_field = models.TextField(blank=True, null=True)
not_important_field = models.TextField(blank=True, null=True)
tags = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'my_table'
indexes = [
GinIndex(
fields=['very_important_field', 'also_important_field', 'not_so_important_field', 'not_important_field', 'tags'],
name='my_table_idx'
)
]
But nothing seems to have changed. The lookup takes exactly the same amount of time as before.
This is the lookup script:
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
# other unrelated stuff here
vector = SearchVector("very_important_field", weight="A") + \
SearchVector("tags", weight="A") + \
SearchVector("also_important_field", weight="B") + \
SearchVector("not_so_important_field", weight="C") + \
SearchVector("not_important_field", weight="D")
query = SearchQuery(search_string, config="italian")
rank = SearchRank(vector, query, weights=[0.4, 0.6, 0.8, 1.0]). # D, C, B, A
full_text_search_qs = MyEntry.objects.annotate(rank=rank).filter(rank__gte=0.4).order_by("-rank")
What am I doing wrong?
Edit:
The above lookup is wrapped in a function I use a decorator on to time. The function actually returns a list, like this:
#timeit
def search(search_string):
# the above code here
qs = list(full_text_search_qs)
return qs
Might this be the problem, maybe?
You need to add a SearchVectorField to your MyEntry, update it from your actual text fields and then perform the search on this field. However, the update can only be performed after the record has been saved to the database.
Essentially:
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVector, SearchVectorField
class MyEntry(models.Model):
# The fields that contain the raw data.
very_important_field = models.TextField(blank=True, null=True)
also_important_field = models.TextField(blank=True, null=True)
not_so_important_field = models.TextField(blank=True, null=True)
not_important_field = models.TextField(blank=True, null=True)
tags = models.TextField(blank=True, null=True)
# The field we actually going to search.
# Must be null=True because we cannot set it immediately during create()
search_vector = SearchVectorField(editable=False, null=True)
class Meta:
# The search index pointing to our actual search field.
indexes = [GinIndex(fields=["search_vector"])]
Then you can create the plain instance as usual, for example:
# Does not set MyEntry.search_vector yet.
my_entry = MyEntry.objects.create(
very_important_field="something very important", # Fake Italien text ;-)
also_important_field="something different but equally important"
not_so_important_field="this one matters less"
not_important_field="we don't care are about that one at all"
tags="things, stuff, whatever"
Now that the entry exists in the database, you can update the search_vector field using all kinds of options. For example weight to specify the importance and config to use one of the default language configurations. You can also completely omit fields you don't want to search:
# Update search vector on existing database record.
my_entry.search_vector = (
SearchVector("very_important_field", weight="A", config="italien")
+ SearchVector("also_important_field", weight="A", config="italien")
+ SearchVector("not_so_important_field", weight="C", config="italien")
+ SearchVector("tags", weight="B", config="italien")
)
my_entry.save()
Manually updating the search_vector field every time some of the text fields change can be error prone, so you might consider adding an SQL trigger to do that for you using a Django migration. For an example on how to do that see for instance a blog article on Full-text Search with Django and PostgreSQL.
To actually search in MyEntry using the index you need to filter and rank by your search_vector field. The config for the SearchQuery should match the one of the SearchVector above (to use the same stopword, stemming etc).
For example:
from django.contrib.postgres.search import SearchQuery, SearchRank
from django.core.exceptions import ValidationError
from django.db.models import F, QuerySet
search_query = SearchQuery("important", search_type="websearch", config="italien")
search_rank = SearchRank(F("search_vector"), search_query)
my_entries_found = (
MyEntry.objects.annotate(rank=search_rank)
.filter(search_vector=search_query) # Perform full text search on index.
.order_by("-rank") # Yield most relevant entries first.
)
I'm not sure but according to postgresql documentation (https://www.postgresql.org/docs/9.5/static/textsearch-tables.html#TEXTSEARCH-TABLES-INDEX):
Because the two-argument version of to_tsvector was used in the index
above, only a query reference that uses the 2-argument version of
to_tsvector with the same configuration name will use that index. That
is, WHERE to_tsvector('english', body) ## 'a & b' can use the index,
but WHERE to_tsvector(body) ## 'a & b' cannot. This ensures that an
index will be used only with the same configuration used to create the
index entries.
I don't know what configuration django uses but you can try to remove first argument
Here are the relevant models:
class WhatToWearCandidates(models.Model):
profile = models.ForeignKey(Profile, null=False, blank=False, related_name="outfit_candidates")
look = models.ForeignKey(StyleLook, null=False, blank=False, related_name="outfit_candidates")
class StyleLook(models.Model):
# Non important attributes
class LookItem(models.Model):
look = models.ForeignKey(StyleLook, null=False, blank=False, related_name="lookitems")
item = models.ForeignKey(Product, null=False, blank=False, related_name="looks")
I'll explain this, each WhatToWearCandidates has a StyleLook and Profile, for each profile we show the correct looks to them. StyleLook just contains details about itself.
Each StyleLook is composed of Products, in the table LookItem we connect which StyleLooks contain which Products.
QUESTION: I'm trying to collect the WhatToWearCandidates that contain 4 or fewer Products efficiently.
I'm trying to use django's annotate() class
all_candidates = WhatToWearCandidates.objects.filter(
look__lookitems__item__assignment=assignment.id, # This is to filter based on Products that belong in the current Assignment
profile_id=1, # Example profile
look_id=15 # Testing with 1 single look for the proper profile
).values('look_id').annotate(lcount=Count('look__lookitems'))
From the debugger all_candidates prints to [{'look__id': 15L, 'lcount': 1}]. I know that this look contains 6 products, so I expected lcount to equal 6.
To double check I tried a similar query from StyleLook instead.
StyleLook.objects.filter(id__in=[15]).values('id').annotate(lcount=Count('lookitems'))
This returns [{'id': 15L, 'lcount': 6}].
What am I doing wrong? How do I get lcount to equal 6 in the WhatToWearCandidates query?
I think you're probably running into problems with default ordering in one of your models. Try adding .order_by() to the end of the query:
all_candidates = WhatToWearCandidates.objects.filter(
look__lookitems__item__assignment=assignment.id,
profile_id=1,
).values('look_id').annotate(lcount=Count('look__lookitems')).order_by()
I've looked at doing a query using an extra and/or annotate but have not been able to get the result I want.
I want to get a list of Products, which has active licenses and also the total number of available licenses. An active license is defined as being not obsolete, in date, and the number of licenses less the number of assigned licenses (as defined by a count on the manytomany field).
The models I have defined are:
class Vendor(models.Model):
name = models.CharField(max_length=200)
url = models.URLField(blank=True)
class Product(models.Model):
name = models.CharField(max_length=200)
vendor = models.ForeignKey(Vendor)
product_url = models.URLField(blank=True)
is_obsolete = models.BooleanField(default=False, help_text="Is this product obsolete?")
class License(models.Model):
product = models.ForeignKey(Product)
num_licenses = models.IntegerField(default=1, help_text="The number of assignable licenses.")
licensee_name = models.CharField(max_length=200, blank=True)
license_key = models.TextField(blank=True)
license_startdate = models.DateField(default=date.today())
license_enddate = models.DateField(null=True, blank=True)
is_obsolete = models.BooleanField(default=False, help_text="Is this licenses obsolete?")
licensees = models.ManyToManyField(User, blank=True)
I have tried filtering by the License model. Which works, but I don't know how to then collate / GROUP BY / aggregate the returned data into a single queryset that is returned.
When trying to filter by procuct, I can quite figure out the query I need to do. I can get bits and pieces, and have tried using a .extra() select= query to return the number of available licenses (which is all I really need at this point) of which there will be multiple licenses associated with a product.
So, the ultimate answer I am after is, how can I retrieve a list of available products with the number of available licenses in Django. I'd rather not resort to using raw as much as possible.
An example queryset that gets all the License details I want, I just can't get the product:
License.objects.annotate(
used_licenses=Count('licensees')
).extra(
select={
'avail_licenses': 'licenses_license.num_licenses - (SELECT count(*) FROM licenses_license_licensees WHERE licenses_license_licensees.license_id = licenses_license.id)'
}
).filter(
is_obsolete=False,
num_licenses__gt=F('used_licenses')
).exclude(
license_enddate__lte=date.today()
)
Thank you in advance.
EDIT (2014-02-11):
I think I've solved it in possibly an ugly way. I didn't want to make too many DB calls if I can, so I get all the information using a License query, then filter it in Python and return it all from inside a manager class. Maybe an overuse of Dict and list. Anyway, it works, and I can expand it with additional info later on without a huge amount of risk or custom SQL. And it also uses some of the models parameters that I have defined in the model class.
class LicenseManager(models.Manager):
def get_available_products(self):
licenses = self.get_queryset().annotate(
used_licenses=Count('licensees')
).extra(
select={
'avail_licenses': 'licenses_license.num_licenses - (SELECT count(*) FROM licenses_license_licensees WHERE licenses_license_licensees.license_id = licenses_license.id)'
}
).filter(
is_obsolete=False,
num_licenses__gt=F('used_licenses')
).exclude(
license_enddate__lte=date.today()
).prefetch_related('product')
products = {}
for lic in licenses:
if lic.product not in products:
products[lic.product] = lic.product
products[lic.product].avail_licenses = lic.avail_licenses
else:
products[lic.product].avail_licenses += lic.avail_licenses
avail_products = []
for prod in products.values():
if prod.avail_licenses > 0:
avail_products.append(prod)
return avail_products
EDIT (2014-02-12):
Okay, this is the final solution I have decided to go with. Uses Python to filter the results. Reduces cache calls, and has a constant number of SQL queries.
The lesson here is that for something with many levels of filtering, it's best to get as much as needed, and filter in Python when returned.
class ProductManager(models.Manager):
def get_all_available(self, curruser):
"""
Gets all available Products that are available to the current user
"""
q = self.get_queryset().select_related().prefetch_related('license', 'license__licensees').filter(
is_obsolete=False,
license__is_obsolete=False
).exclude(
license__enddate__lte=date.today()
).distinct()
# return a curated list. Need further information first
products = []
for x in q:
x.avail_licenses = 0
x.user_assigned = False
# checks licenses. Does this on the model level as it's cached so as to save SQL queries
for y in x.license.all():
if not y.is_active:
break
x.avail_licenses += y.available_licenses
if curruser in y.licensees.all():
x.user_assigned = True
products.append(x)
return q
One strategy would be to get all the product ids from your License queryset:
productIDList = list(License.objects.filter(...).values_list(
'product_id', flat=True))
and then query the products using that list of ids:
Product.objects.filter(id__in=productIDList)