django 'null value in column' - python

I try to save user's birth date, but get "null value in column "dob" violates not-null constraint" error.
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, unique=True)
nickname = models.CharField(max_length=32)
dob = models.DateField(null=False)
sex = models.BooleanField(null=False)
Here i try to generate random users:
def create_random_users(userCount=1000):
random.seed()
for i in range(0, userCount):
sex = random.randint(0, 1)
name = random.choice(names[sex])
email = "{0}{1}#mail.com".format(name, i)
user = soc_models.User.objects.create_user(email, email, password='password')
user.save()
userProfile = soc_models.Profile.objects.create()
userProfile.user = user
_year = random.randrange(1962, 1995)
_month = random.randrange(1, 12)
_day = random.randrange(1, calendar.monthrange(_year, _month)[1])
userProfile.dob = datetime.datetime(_year, _month, _day)
userProfile.sex = random.randrange(0, 1)
userProfile.city = random.randrange(4000000)
userProfile.country = random.randrange(230)
userProfile.save()
Thank you.

The create method is documented as "a convenience method for creating an object and saving it all in one step". So when the following statement in your sample data creation script runs:
userProfile = soc_models.Profile.objects.create()
It attempts to save an empty Profile object to the database. Since you haven't set the dob attribute at this point, you trigger the NOT NULL constraint.
Two ways to avoid this are:
create the object via the constructor so that it isn't immediately saved to the database.
provide values for all the fields via keyword arguments to create.

When using create, you must pass all required values to it, if you want to call save() later, use model constructor instead, i.e.:
userProfile = soc_models.Profile()

I'm not sure whether it fixes your error or not, but regarding to docs, DateField should be used for storing datetime.date instance, and DateTimeField to store datetime.datetime.
P.S. Really, it looks like you trying to "migrate" DB scheme (to change already created columns). Django doesn't support such feature, but you can use external applications, like South.

Related

how to populate user specific data in django

I have 2 models employee and leave, where Employee is the foreign key in Leave. I want to display the leave requests by a specific employee on a page when they log in.
i'm not able to get the leave data populated on, and if there are more than 2 leaves applied by a single employee i get an error saying 2 items found
here is my code
models.py
class Employee(models.Model):
user=models.OneToOneField(User, on_delete=models.CASCADE)
no_of_leaves=models.IntegerField(
null=False,
validators=[
MinValueValidator(1),
MaxValueValidator(24)
],
default=24
)
def __str__(self):
return self.user.username
class Leave(models.Model):
employee=models.ForeignKey(Employee,on_delete=models.CASCADE,default="")
start_date=models.DateField(auto_now_add=False)
end_date=models.DateField(auto_now_add=False)
req_date=models.DateTimeField(default=datetime.datetime.now())
STATUS_OPTIONS = (
("Approve","Approve"),
("Pending","Pending"),
("Decline","Decline"),
)
approved=models.CharField(max_length=10,choices=STATUS_OPTIONS,default='Pending')
def __str__(self):
return self.employee.user.username
#property
def date_diff(self):
return (self.start_date - self.end_date).days
views.py
def home(request):
user = request.user
u = User.objects.get(username=user)
e = Employee.objects.get(user=user.id)
leave = Leave.objects.get_or_create(employee=e)
print(leave)
nofleaves=None
if user.is_superuser:
pass
else:
nofleaves=u.employee.no_of_leaves
context={'nofleaves':nofleaves,'leave':leave,}
return render(request,"leaveApp/home.html",context)
Just like #Jao said get is to fetch only one data while there is a many to one relationship between Leave and Employee that means there can be multiple Leave for one employee thus get will throw an error.
You should use filter:
Change this line
leave = Leave.objects.get_or_create(employee=e) to something like
leave = Leave.objects.filter(employee=e.id)
if not leave.exists():
leave = Leave.objects.create(employee=e)
This other question might help.
The thing is you should use get for only one data. You shouldn't use get on a many-to-one relathionship. The logic is, there can be multiple Leave by Employee so you can't have a consistent use of get.
What you can use is filter but I'd use related names/related fields which will allow you to use employee.leaves if you define it correctly.

How to query database items from models.py in Django?

I have different model. Choices of Multiselctfield of one model is dependent on another model.So , database has to be queried inside model.py While doing so, this causes problem in migration. (Table doesn't exist error)
class Invigilator(models.Model):
---
# this method queries Shift objects and Examroom
def get_invigilator_assignment_list ():
assignment = []
shifts = Shift.objects.all()
for shift in shifts:
rooms= ExamRoom.objects.all()
for room in rooms:
assign = str (shift.shiftName)+ " " +str (room.name)
assignment.append (assign)
return assignment
assignment_choice = []
assign = get_invigilator_assignment_list()
i = 0
for assignm in assign:
datatuple = (i,assignm)
assignment_choice.append(datatuple)
i= i+1
ASSIGNMENT_CHOICE = tuple(assignment_choice)
assignment =MultiSelectField (choices = ASSIGNMENT_CHOICE, blank = True, verbose_name="Assignments")
You cannot add dynamic choices because they are all stored in the migration files and table info. If Django lets you do that, this means that everytime someone adds a record to those 2 models, a new migration should be created and the db should be changed. You must approach this problem differently.
As far as I know django-smart-selects has a ChainedManyToMany field which can do the trick.
Here is an example from the repo.
from smart_selects.db_fields import ChainedManyToManyField
class Publication(models.Model):
name = models.CharField(max_length=255)
class Writer(models.Model):
name = models.CharField(max_length=255)
publications = models.ManyToManyField('Publication', blank=True, null=True)
class Book(models.Model):
publication = models.ForeignKey(Publication)
writer = ChainedManyToManyField(
Writer,
chained_field="publication",
chained_model_field="publications")
name = models.CharField(max_length=255)
This cannot be done in the model and doesn't make sense. It's like you're trying to create a column in a table with a certain fixed set of choices (what is MultiSelecField anyway?), but when someone later adds a new row in the Shift or ExamRoom table, the initial column choices have to change again.
You can
either make your assignment column a simple CharField and create the choices dynamically when creating the form
or you can try to model your relationships differently. For example, since it looks like assignment is a combination of Shift and ExamRoom, I would create a through relationship:
shifts = models.ManyToManyField(Shift, through=Assignment)
class Assignment(Model):
room = ForeignKey(ExamRoom)
shift = ForeignKey(Shift)
invigilator = ForeignKey(Invigilator)
When creating the relationship, you'd have to pick a Shift and a Room which would create the Assignment object. Then you can query things like invigilator.shifts.all() or invigilator.assignment_set.first().room.

Django ORM Issue

I am learning RestAPI and When I try to post data to update my database columns the modified_on column should automatically populated to current date and time but it is not updating.
I am currently using django cassandra engine ORM where there is no functionality like auto_add_now() or auto_now().
Can any one give a suggestion where am I going wrong?
Model Class:
class Mydb(DjangoCassandraModel):
id = columns.UUID(primary_key=True, default=uuid.uuid4())
user_name = columns.Text()
user_email = columns.Text(default=None)
user_password = columns.Text()
description = columns.Text()
creation_date = columns.DateTime(default=datetime.datetime.today(), static=True)
modified_on = columns.DateTime(default=datetime.datetime.today())
My Serialization class:
class TaskSerializer(serializers.Serializer):
# id = serializers.UUIDField(default=uuid.uuid4)
USER_ID = serializers.UUIDField(default= uuid.uuid4(),source='id')
# user_name = serializers.CharField(max_length=50)
USER_NAME_FIELD = serializers.CharField(max_length=50, source='user_name')
USER_EMAIL = serializers.CharField(source='user_email')
USER_PASSWORD = serializers.CharField(max_length=20, source='user_password')
EXPLANATION = serializers.CharField(max_length=100, source='description')
MODIFIED_AT = serializers.DateTimeField(default=datetime.datetime.today(), source='modified_on')
CREATED_ON = serializers.DateTimeField(default=datetime.datetime.today(), source='creation_date')
def create(self, validated_data):
return Mydb.objects.create(**validated_data)
def update(self, instance, validated_data):
# instance.id = validated_data.get('id', instance.id)
instance.user_name = validated_data.get('user_name', instance.user_name)
instance.user_email = validated_data.get('user_email', instance.user_email)
instance.user_password = validated_data.get('user_password', instance.user_password)
instance.description = validated_data.get('description',instance.description)
instance.modified_on = validated_data.get('modified_on', instance.modified_on)
instance.save()
# instance.creation_date = validated_data.get('creation_date', instance.creation_date)
You should rather use utils now for timezone aware times
from django.utils.timezone import now
also in model you should set function not evaluated value ( no parenthesis after now )
MODIFIED_AT = serializers.DateTimeField(default=now, source='modified_on')
MODIFIED_AT = serializers.DateTimeField(default=datetime.datetime.today(), source='modified_on')
to
MODIFIED_ON = serializers.DateField(default=datetime.datetime.today(), source='modified_on')
change MODIFIED_AT to MODIFIED_ON
You can try:
create_date = models.DateField(auto_now_add=True,
verbose_name=u'Create date')
update_date = models.DateTime(auto_now=True,
verbose_name=u'Update date')
auto_now_add automatically set the field to now when the object is first created.
auto_now=True automatically set the field to now every time the object is saved.
Doc is here.
Please make sure to add the auto_now=True for your modified_at filed, in your model.
It automatically sets the field to now every time the object is saved. Useful for “last-modified” timestamps. Note that the current date is always used; it’s not just a default value that you can override.
Example:
class Mydb(DjangoCassandraModel):
creation_date = columns.DateTime(auto_now_add=True)
modified_on = columns.DateTime(auto_now=True)
Docs Here:
https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.DateField.auto_now
You use default=datetime.datetime.today() as your default value for the fields. Since you call the function immediately (by adding ()), the function is called exactly once on the first load of the code and the datetime at that moment is put into the default value and not updated until you reload the code (a.k.a. restart the server).
If you want to always use the then current time, leave away the () to cause Django to call the function each time. default=datetime.datetime.today
It's preferable for you to use now though, like iklinac did in his answer, as that also respects your timezone settings. His anwer also leaves out the parenteses, yielding the correct result.
from django.utils.timezone import now
...
MODIFIED_AT = serializers.DateTimeField(default=now, source='modified_on')

django - creating users making their fullname unique

i need small help in the logic of creating user making his name unique:
i have a django user profile. i am creating users in this way:
fullname = request.POST.get('fullname')
random_username = ''.join(random.sample(string.ascii_lowercase, 8))
new_user = User.objects.create_user(random_username, email, passwort)
##update siteprofile of this new user
userprofile = new_user.get_profile()
"""
i need to make this fullname unique with this logic:
for example john is the fullname of new user. i need to check if there are
other johns in db, if there is another user with this name, i will name the
user with 'john1'. if there are 2, the new user will get the name 'john3'
how can I check this in db in some efficient way?
"""
userprofile.name = fullname
userprofile.save()
You want to check for the IntegrityError on save and update accordingly. Doing a query to check for the names existence creates a race condition where you can search then two separate threads try to create the same fullname at the same time.
from django.db import transaction
#transaction.commit_manually
def set_fullname(userprofile, fullname, i=0):
new_fullname = u"{}{}".format(fullname, str(i) if i else '')
try:
userprofile.fullname = new_fullname
userprofile.save()
transaction.commit()
return userprofile
except IntegrityError:
transaction.rollback()
i += 1
# Just recursively try until we a valid name. This could be problematic if you
# have a TON of users, but in that case you could just the filter before then to see
# what number to start from.
return set_fullname(userprofile, fullname, i)
userprofile = set_fullname(userprofile, fullname)
For this purpose it would be better to use form https://docs.djangoproject.com/en/dev/topics/forms/. But if you won't to use forms, you can do it that way:
i = 0
orig_fullname = fullname
created = False
while not created:
profile, created = UserProfile.objects.get_or_create(name=fullname)
if not created:
i+=1
fullname = orig_fullname + str(i)
# there you have new user's profile
Note, that field 'name' in UserProfile model must have unique=True parameter https://docs.djangoproject.com/en/dev/ref/models/fields/#unique

unique=True not working when the Modelform is passed a custom dictionary?

So right now I'm editting the Querydict that the modelform gives to the view to make the submission in one field all lowercase and have no spaces. but then when I construct and pass that dictionary back to the Modelform to be validated/saved it doesn't give me an error if the same thing has been entered more than once. It seems like unique=True should work for all submissions that are in the correct format not just ones from request.POST. Any help/insight on the issue would be awesome.
EDIT: CODE
THE VIEW THAT HANDLES THE MODELFORM
dict = {}
sitename = request.POST['sitename']
#insert an if statement telling them only letters are allowed
urltitle = ''.join(sitename.split()).lower()
dict['sitename'] = urltitle
make = MakesiteForm(dict)
if make.is_valid():
make.save()
MODEL IN QUESTION
class Makesite(models.Model):
sitename = models.CharField(max_length=100, unique = True)
siteinfo = models.ManyToManyField(Siteinfo)
ref_id = models.ManyToManyField(RefID)
def __unicode__(self):
return u'%s' %(self.sitename)
1.Don't reassign built-in dict function
2.Field processing logic should be done in clean method:
class MakesiteForm(forms.ModelForm):
# your code... Then
def clean_sitename(self):
sitename = self.cleaned_data['sitename']
return ''.join(sitename.split()).lower()
3.Show what errors you get if form is not valid?

Categories