What I want is to generate a string(key) of size 5 for my users on my website. More like a BBM PIN.
The key will contain numbers and uppercase English letters:
AU1B7
Y56AX
M0K7A
How can I also be at rest about the uniqueness of the strings even if I generate them in millions?
In the most pythonic way possible, how can I do this?
My favourite is
import uuid
uuid.uuid4().hex[:6].upper()
If you using django you can set the unique constrain on this field in order to make sure it is unique. https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.unique
From 3.6 You can use secrets module to generate nice random strings.
https://docs.python.org/3/library/secrets.html#module-secrets
import secrets
print(secrets.token_hex(5))
A more secure and shorter way of doing is using Django's crypto module.
from django.utils.crypto import get_random_string
code = get_random_string(5)
get_random_string() function returns a securely generated random string, uses
secrets module under the hood.
You can also pass allowed_chars:
from django.utils.crypto import get_random_string
import string
code = get_random_string(5, allowed_chars=string.ascii_uppercase + string.digits)
Am not sure about any short cryptic ways, but it can be implemented using a simple straight forward function assuming that you save all the generated strings in a set:
import random
def generate(unique):
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
while True:
value = "".join(random.choice(chars) for _ in range(5))
if value not in unique:
unique.add(value)
break
unique = set()
for _ in range(10):
generate(unique)
If you can afford to lose '8' and '9' in the generated numbers there is a very pythonic solution to getting a random number.
import os
import base64
base64.b32encode(os.urandom(3))[:5].decode('utf-8')
Since you are going for uniqueness then you have a problem since 36 * 36 * 36 * 36 * 36 = 60'466'176 which will definitely result in collisions if you have millions. Since sets are faster than dicts we do...
some_set = set()
def generate():
return base64.b32encode(os.urandom(3))[:5].decode('utf-8')
def generate_unique():
string = generate()
while string in some_set:
string = generate()
some_set.add(string)
return string
However since uniqueness is usually more important I'd recommend generating a unique code for each of the numbers from 0 to 36^5 - 1 like this. We can use a large prime and modulo to make a psuedo-random number like this.
import base64
import math
num = 1
prime_number = 60466181
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'
def num_to_code(n: int):
string = ''
hashed = hash_number(n)
for x in range(5):
charnumber = hashed % 36
hashed = math.floor(hashed / 36)
string += characters[charnumber]
return string
def hash_number(n: int, rounds = 20):
if rounds <= 0:
return n
hashed = (n * prime_number) % (36 ** 5)
return hash_number(hashed, rounds - 1)
if __name__ == '__main__':
code = num_to_code(1)
print(code)
Here are the results from generating 0-5, they'll always generate the same sequence.
0 AAAAA (easily fixable ofc)
1 ZGQR9
2 ON797
3 DUMQ6
4 31384
5 R8IP3
If you have a way of associating each user to a unique ID (for example Primary Key in Django or Flask). You can do something like this:
Note: This does not generate a fixed length.
We will pad the user_id to the right to make the generated length a bit static
import os
import base64
user_id = 1
#pad the string
number_generate = str(user_id).rjust(5,"0")
base64.b32encode(bytes(number_generate, 'utf-8')).decode('utf-8').replace('=','')
size = 5
''.join(random.choice(string.letters[26:] + string.digits) for in range(size))
this will generate some short code, but they can be duplicated. so check if they are unique in your database before saving.
def generate(size=5):
code = ''.join(random.choice(string.letters[26:] + string.digits) for in range(size))
if check_if_duplicate(code):
return generate(size=5)
return code
or using django unique constrain, and handle exceptions.
There is a function in django that does what you're looking for (credits to this answer):
Django provides the function get_random_string() which will satisfy
the alphanumeric string generation requirement. You don't need any
extra package because it's in the django.utils.crypto module.
>>> from django.utils.crypto import get_random_string
>>> unique_id = get_random_string(length=32)
>>> unique_id
u'rRXVe68NO7m3mHoBS488KdHaqQPD6Ofv'
You can also vary the set of characters with allowed_chars:
>>> short_genome = get_random_string(length=32, allowed_chars='ACTG')
>>> short_genome
u'CCCAAAAGTACGTCCGGCATTTGTCCACCCCT'
I have a unique field, named 'systemCode' within a lot of my models. And I am generating this manually, but also sometimes it can take value from user input, so I have to check this value before saving and if it matches , regenerating this value as a unique value.
And this is how I generate unique strings at this scenario :
This is my standard class Model :
class ClassOne(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
I am using save() method to generate and check this systemCode is unique :
def save(self, *args, **kwargs):
systemCode = self.systemCode
if not systemCode:
systemCode = uuid.uuid4().hex[:6].upper()
while ClassOne.objects.filter(systemCode=systemCode).exclude(pk=self.pk).exists():
systemCode = uuid.uuid4().hex[:6].upper()
self.systemCode = systemCode
super(ClassOne, self).save(*args, **kwargs)
But I have same systemCode field in all my Models. So I am using a function to generate value.
So, this is how to generate unique value for all models using saveSystemCode() function :
import uuid
def saveSystemCode(inClass, inCode, inPK, prefix):
systemCode = inCode
if not systemCode:
systemCode = uuid.uuid4().hex[:6].upper()
while inClass.objects.filter(systemCode=systemCode).exclude(pk=inPK).exists():
systemCode = uuid.uuid4().hex[:6].upper()
return systemCode
class ClassOne(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
self.systemCode = saveSystemCode(ClassOne, self.systemCode, self.pk, 'one_')
super(ClassOne, self).save(*args, **kwargs)
class ClassTwo(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
self.systemCode = saveSystemCode(ClassTwo, self.systemCode, self.pk, 'two_')
super(ClassTwo, self).save(*args, **kwargs)
class ClassThree(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
self.systemCode = saveSystemCode(ClassThree, self.systemCode, self.pk, 'three_')
super(ClassThree, self).save(*args, **kwargs)
while loop in the 'saveSystemCode' function is preventing to save same value again.
To generate unique one you can use below command:
import uuid
str(uuid.uuid1())[:5]
Here a solution to gen codes of lenght 5 or any on a file:
import shortuuid as su
n = int(input("# codes to gen: "))
l = int(input("code lenght: "))
shou = su.ShortUUID(alphabet="QWERTYUIOPASDFGHJKLZXCVBNM0123456789")
codes = set()
LEN_CNT = 0
with open('file.txt', 'w') as file:
while len(codes) < n:
cd = shou.random(length=l)
codes.add(cd)
if len(codes) > LEN_CNT:
LEN_CNT = len(codes)
file.write(f"{cd}\n")
(shortuuid sometimes gen duplicated codes, so I use a set to deal with that)
As the time of writing this answer, there is an actively maintained package that generates short UUIDs:
https://github.com/skorokithakis/shortuuid
For Django support, have a look here:
https://github.com/skorokithakis/shortuuid#django-field
Related
import random
from random import randint as rand
upperCase = chr(rand(65,66))
lowerCase = chr(rand(97,122))
class PasswordLetters:
def __init__(self):
pass
def generateCapitalCaseLetter(self):
uppCase = chr(rand(65,91))
return uppCase
def generateLowerCaseLetter(self):
lowCase = chr(rand(97,122))
return lowCase
def generateSpecialLetter(self):
specLet = random.choice(specialCharacters)
return specLet
def generateNumber(self):
num = rand(1,99)
class PasswordGenerator:
def __init__(self,uppCase,lowCase,specLet,num):
self.uppCaseList = []
lowCaseList = []
specLet = []
numList = []
self.passLetter = PasswordLetters()
for i in range(0,uppCase):
self.uppCaseList.append(self.passLetter.generateCapitalCaseLetter)
password = PasswordGenerator(1,1,1,1)
password.uppCaseList
So The Problem I am facing is when I try to get uppCaseList back from my password object it comes back to me as an method in a list instead of a letter in a list. I think the problem is in my PasswordLetters class but I can't figure it out.
The only thing I want is for
password.uppCaseList
to return a list with letters
Since it's a function you are calling, it would need to include the ( open and close ) parentheses. You may refer here for explanation.
for i in range(0,uppCase):
self.uppCaseList.append(self.passLetter.generateCapitalCaseLetter())
For my E-commerce project, I am trying to generate a reference code that can be understandable yet unique at the same time:
I am trying to generate a reference code that after each purchase is made that includes that day, month, year, hour, minute and a digit that increases with a new transaction
DDMMYYHHMMXXX
Day, Month, Year, Hour, Minute,3 digits starting with 001 and increasing with each new order.
How do I do it?
My current code generated is:
def create_ref_code():
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
model.py
class Order(models.Model):
ref_code = models.CharField(max_length=20, blank=True, null=True)
ordered_date = models.DateTimeField()
def __str__(self):
return self.user.username
This is how far I have reached but I am not sure how to increase the count with every new order
def create_ref_code():
now = datetime.now()
code = now.strftime("%y%m%d%H%M%S")
print(code)
count = + 1
digit = str(count).zfill(3)
my_code = (code, digit)
return ''.join(my_code)
for that you can extend the save method and retrieve all the order count and also you can use something like this to pad the leading zeroes on that count
str(1).zfill(3)
this will create 001 output in string and you need this in string format to concat the data so no need to convert that to integer again
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
count = ***retrieve you count of that orders using query*** + 1
digit = str(count).zfill(3)
self.reference_code = your logic to create reference code
updated:
you don't have to increment count like that
def create_ref_code():
now = datetime.now()
"""
'make query to count all todays order here'
count = Order.objects.filter(filter argument by date).count() + 1
"""
code = now.strftime("%y%m%d%H%M%S")
digit = str(count).zfill(3)
my_code = (code, digit)
return ''.join(my_code)
instead of DDMMYYHHMMXXX try UUID-4
code :
import uuid
uuid.uuid4()
I am trying to expire tokens after its creation with a max duration of 1 minute to meet security requirements. my function looks like this, but I don't think is doing it the right way, and I Would like to know what is the best way to expire the token after 1 minute? I am using the technique of diffing two times. the following function works under models.py
def is_token_expired(self):
if self.token == None:
return False
now = datetime.datetime.utcnow().replace(tzinfo=utc)
timediff = now - self.created_at
if timediff.seconds / 60 - 1 > 0:
return True
return False
I think the elegant way to archive your goal is leveraging django cache.
Sample code:
class Foo(models.Model):
...
def save(self, *args, **kwargs):
if not self.pk:
# save the token when record created
cache.set('token_key', '<Your token>', timeout=60)
super(Foo, self).save(*args, **kwargs)
#property
def is_token_expired(self):
# check if the token expired
return cache.get('token_key') is None
#property
def token(self):
# get token
return cache.get('token_key')
It is better to use #property in your model:
from datetime import timedelta
class Foo(models.Model):
some_field = models.CharField()
creation_date = models.DateTimeField(auto_now_add=True)
#property
def is_expired(self):
if datetime.now > (self.creation_date + timedelta(minutes=1)):
return True
return False
you can change timedelta(minutes=1) to amount that your token is valid.
and use it in your code like this:
if your_instance.is_expired == True:
# do something
You can also use Django builtin cache system (that Enix mentioned) as a better approach.
I am trying to calculate a value from GraphQL. I am sending mutation to Django models but before save it I want to calculate this value with if statement (if the value is greater than 10 divide by 2, if is less than 10 multiply by 2).
I don't know where to add this function.
Here is my mutation in schema.py
class CreatePrice(graphene.Mutation):
price = graphene.Field(PriceType)
class Arguments:
price_data = PriceInput(required=True)
#staticmethod
def mutate(root, info, price_data):
price = Price.objects.create(**price_data)
return CreatePrice(price=price)
class Mutation(graphene.ObjectType):
create_product = CreateProduct.Field()
create_price = CreatePrice.Field()
schema = graphene.Schema(query = Query, mutation=Mutation)
And here is my Django model. Base price is calculated value and function name has two options(*2 or /2 it depends of initial value).
class Price(models.Model):
base_price = models.CharField(max_length = 20)
function_name = models.CharField(max_length = 20, choices = PROMO_FUNCTION)
def __str__(self):
return self.price_name
P.S. Sorry for bad English. Thanks!
I don't know why you are using CharField for base_price. So, I suggest you to do this:
#staticmethod
def mutate(root, info, price_data):
if int(price_data.base_price) >= 10:
price_data.base_price = str(int(price_data.base_price) / 2)
else:
price_data.base_price = str(int(price_data.base_price) * 2)
price = Price(base_price=price_data.base_price, function_name=price_data.function_name)
price.save()
return CreatePrice(price=price)
You can also create records in database by creating object and using save method on it.
I'm new to python REST API and so facing some particular problems. I want that when I enter the input as pathlabid(primary key), I want the corresponding data assigned with that key as output. When I run the following code i only get the data corresponding to the first row of table in database even when the id i enter belong to some other row.
This is the VIEWS.PY
class pathlabAPI(View):
#csrf_exempt
def dispatch(self, *args, **kwargs):
# dont worry about the CSRF here
return super(pathlabAPI, self).dispatch(*args, **kwargs)
def post(self, request):
post_data = json.loads(request.body)
Pathlabid = post_data.get('Pathlabid') or ''
lablist = []
labdict = {}
lab = pathlab()
labs = lab.apply_filter(Pathlabid = Pathlabid)
if Pathlabid :
for p in labs:
labdict["Pathlabid"] = p.Pathlabid
labdict["name"] = p.Name
labdict["email_id"] = p.Emailid
labdict["contact_no"] = p.BasicContact
labdict["alternate_contact_no"] = p.AlternateContact
labdict["bank_account_number"] = p.Accountnumber
labdict["ifsccode"] = p.IFSCcode
labdict["country"] = p.Country
labdict["homepickup"] = p.Homepickup
lablist.append(labdict)
return HttpResponse(json.dumps(lablist))
else:
for p in labs:
labdict["bank_account_number"] = p.Accountnumber
lablist.append(labdict)
return HttpResponse(json.dumps(lablist))
There are a number of issues with the overall approach and code but to fix the issue you're describing, but as a first fix I agree with the other answer: you need to take the return statement out of the loop. Right now you're returning your list as soon as you step through the loop one time, which is why you always get a list with one element. Here's a fix for that (you will need to add from django.http import JsonResponse at the top of your code):
if Pathlabid:
for p in labs:
labdict["Pathlabid"] = p.Pathlabid
labdict["name"] = p.Name
labdict["email_id"] = p.Emailid
labdict["contact_no"] = p.BasicContact
labdict["alternate_contact_no"] = p.AlternateContact
labdict["bank_account_number"] = p.Accountnumber
labdict["ifsccode"] = p.IFSCcode
labdict["country"] = p.Country
labdict["homepickup"] = p.Homepickup
lablist.append(labdict)
else:
for p in labs:
labdict["bank_account_number"] = p.Accountnumber
lablist.append(labdict)
return JsonResponse(json.dumps(lablist))
As suggested in the comments, using Django Rest Framework or a similar package would be an improvement. As a general rule, in Django or other ORMs, you want to avoid looping over a queryset like this and adjusting each element. Why not serialize the queryset itself and do the logic that's in this loop in your template or other consumer?
You are return the response in for loop so that loop break on 1st entry
import json
some_list = []
for i in data:
some_list.append({"key": "value"})
return HttpResponse(json.dumps({"some_list": some_list}), content_type="application/json")
Try above example to solve your problem