I want to generate an unique string in my django1.6 application. For that I want to concatenate the primary key (autofield) with the string. My code is
class Complaints(models.Model):
gid = models.AutoField(primary_key=True)
complaint_no = models.CharField(max_length=50)
status = models.IntegerField(default=0,blank=False,null=False)
def __str__(self):
return ('%s %s' % (self.gid, self.complaint_no))
def save(self, force_insert=False, force_update=False):
self.complaint_no = '%s%d' % (self.complaint_no,self.gid)
super(Complaints, self).save(force_insert, force_update)
class Meta:
db_table = 'complaints'
verbose_name = "complaints"
But I got the error message as
TypeError at /callcentre/register/
%d format: a number is required, not NoneType
Please help me to solve this error!
Create field id as charfield e.g.
string_id = models.CharField(max_length=100, default='file')
Make a function that make string id when the model is saved.
def save(self):
new_id = self.id
self.string_id = str(new_id) + '_dot'
super(YourModelName, self).save()
You need to call super() first.
As you can read in the documentation:
There’s no way to tell what the value of an ID will be before you call save(), because that value is calculated by your database, not by Django.
When you call super().save(), the database engine calculates the id (or gid in your case, but please reconsider using the provided id) and thus allows you to use it. Before that it is None.
You may check this topic as well.
Related
I am try to refer 'spot_price' of model 'Spot' in model 'Basis' in django model layer, How can I manage this?
I have designed view.py to automaticaly render the templates. so I am not able to modifty any view.py to choose data like 'models.B.objects.get().field'.
and more, str is set to indicate the date, so, in the django backstage admin, the 'spot' field display would be 'date' formate, how would be change to 'spot_price'?
model Spot
class Spot(models.Model):
date = models.DateField(primary_key=True)
spot_price = models.FloatField(blank=True, null=True)
def __str__(self):
return str(self.date) if self.date else ''
need to refer the model Spot'spot_price by date, cause date is unique but spot_price is not
class Basis(models.Model):
date = models.DateField(primary_key=True)
major_future_contract_close_price = models.FloatField(blank=True)
spot = models.OneToOneField(Spot, on_delete=models.CASCADE)
basis = models.FloatField(default=calculate_basis)
def __str__(self):
return str(self.date) if self.date else ''
def calculate_basis(self):
return abs(self.major_future_contract_close_price -
self.spot.spot_price)
I expect the Basis.query.data would to like 'date: 2019-04-25, major_future_contract_close_price: 100.0, spot: 96.5, basis: 3.5'
You can't use class method as default, because it requires self, which is not existing when you are still creating the object.
If you need to have it stored in field (database), override default save() method or use signals to modify the basis field once your object is created. Also note that you have to recalculate basis every time close_price or spot_price changes, as the value is just written in database.
Probably better solution would be to use #property so for anyone that will be using you model it will look like a field, but it will dynamically calculate value based on current data.
For example, I'm overriding save() to calculate the basis field. I set it as editable=False, which means it won't appear in forms by default (and you will not be able to see it in admin!). You can safely remove that part if you want to just look at the values.
Additionally I add basis_as_property property.
class Basis(models.Model):
date = models.DateField(primary_key=True)
major_future_contract_close_price = models.FloatField(blank=True)
spot = models.OneToOneField(Spot, on_delete=models.CASCADE)
basis = models.FloatField(editable=False, blank=True)
#property
def basis_as_property(self):
return '%s' % (self.calculate_basis())
def __str__(self):
return str(self.date) if self.date else ''
def save(self, *args, **kwargs):
if not self.basis:
self.basis = self.calculate_basis()
super(Basis, self).save(*args, **kwargs)
def calculate_basis(self):
return abs(self.major_future_contract_close_price - self.spot.spot_price)
As for Spot str repr, I don't think it's possible to change it based on where it is referenced. If you want to use spot_price, you can just use: return str(self.spot_price) if self.spot_price else str(self.date)
I am working with a pre-existing database called Employee. I have three separate fields i'd like to combine into a single field, but I can't add an additional field to the pre-exisiting database.
I know the proper way to combine multiple fields into one field using python is
'%s - %s %s' % (self.username, self.firstname, self.lastname)
However, I can't call self outside the model, or at least i'm not sure where I would call self.
My end goal is to have a select box with the combined field a user can search either first, last, or account name. My current model looks like the following:
class Employee(models.Model):
staff_id = models.IntegerField(db_column = 'Employee_ID')
status_id = models.IntegerField(db_column = 'StatusID')
username = models.CharField(db_column = 'SamAccountName',primary_key = True, max_length = 31)
lastname = models.CharField(db_column = 'Surname', max_length = 63)
firstname = models.CharField(db_column = 'GivenName', max_length = 63)
title = models.CharField(db_column = 'Title', max_length = 127)
class Meta:
managed = False
db_table = '[Employee]'
I tried to add to my model, but when I call full_username it says the field doesn't exists, which is true because there isn't a field in the database. We aren't allowed to add a new field to the database.
def get_full_name(self):
full_username = '%s - %s %s' % (self.username, self.firstname, self.lastname)
return full_username.split()
Ideally i'd want my view to look something like this (i know it wont' work as is, i'd replace that with 'full_username):
activeuserlist = Employee.objects.filter(staff_id = '1').values_list('%s - %s %s' % (Employee.username, Employee.firstname, Employee.lastname), flat = True)
How would I get the full name added to my view, what am I missing with my logic or where would be the correct place to put it?
You can give this a try:
from django.db.models.functions import Concat
from django.db.models import F, Value
employees = Employee.objects.annotate(full_username=Concat(F('username'), Value(' - '), F('firstname'), Value(' '), F('lastname')))\
.filter(staff_id='1', full_username__icontains='hello')
The icontains bits is just a demo, with this query you can filter the result based on the combined name as well.
If you have to use this everywhere, then I recommend you create your own queryset/manager in your model then put this annotation into the default queryset. After that you can use your full_username filter any where you want without having to add the annotation first.
First, try formatting the string in a good way
full_username = '{} - {} {}'.format(self.username, self.firstname, self.lastname)
Second you can the method get_full_name() in model and call it from the model object.
employee = Employee.objects.get(id=1)
full_name = employee.get_full_name()
That should work. :)
Try using a property:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
#property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
If you don't need the functionality of a QuerySet, try this
activeuserlist = [
'{} - {} {}'.format(user.username, user.firstname, user.lastname)
for user in Employee.objects.filter(staff_id = '1')
]
If you do need a QuerySet, I think it's not possible on python-level, only on SQL-level. See this thread on annotations.
I want to do a query on the django User table like this:
u = User.objects.filter(member__in = member_list)
where:
class Member(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField('Date of Birth', blank=True, null=True)
and member_list is a list of eligible members.
The query works fine but the problem is I do not actually know the model member is called member. It could be called anything.
I store the name of the model I want in a model called Category. I have a link to the name of the model through content_type.Category is defined as:
class Category(models.Model):
name = models.CharField('Category', max_length=30)
content_type = models.ForeignKey(ContentType)
filter_condition = JSONField(default="{}", help_text=_(u"Django ORM compatible lookup kwargs which are used to get the list of objects."))
user_link = models.CharField(_(u"Link to User table"), max_length=64, help_text=_(u"Name of the model field which links to the User table. 'No-link' means this is the User table."), default="No-link")
def clean (self):
if self.user_link == "No-link":
if self.content_type.app_label == "auth" and self.content_type.model == "user":
pass
else:
raise ValidationError(
_("Must specify the field that links to the user table.")
)
else:
if not hasattr(apps.get_model(self.content_type.app_label, self.content_type.model), self.user_link):
raise ValidationError(
_("Must specify the field that links to the user table.")
)
def __unicode__(self):
return self.name
def _get_user_filter (self):
return str(self.content_type.app_label)+'.'+str(self.content_type.model)+'.'+str(self.user_link)+'__in'
def _get_filter(self):
# simplejson likes to put unicode objects as dictionary keys
# but keyword arguments must be str type
fc = {}
for k,v in self.filter_condition.iteritems():
fc.update({str(k): v})
return fc
def object_list(self):
return self.content_type.model_class()._default_manager.filter(**self._get_filter())
def object_count(self):
return self.object_list().count()
class Meta:
verbose_name = _("Category")
verbose_name_plural = _("Categories")
ordering = ('name',)
So I can retrieve the name of the model that links to User but I then need to convert it into a class which I can include in a query.
I can create an object x = category.content_type.model_class() which gives me <class 'cltc.models.Member'> but when I them perform a query s = User.objects.filter(x = c.category.object_list()) I get the error Cannot resolve keyword 'x' into field.
Any thoughts most welcome.
The left hand side of the filter argument is a keyword, not a python object, so x is treated as 'x', and Django expects a field called x.
To get around this, you can ensure that x is a string, and then use the python **kwarg syntax:
s = User.objects.filter(**{x: c.category.object_list()})
Thanks to https://stackoverflow.com/a/4720109/823020 for this.
The Model is defined as below:
class UnitTab(models.Model):
unit_id = models.PositiveIntegerField(primary_key=True)
create_time = models.PositiveIntegerField()
update_time = models.PositiveIntegerField()
read_db_connection = 'game_center_admin_db'
write_db_connection = 'game_center_admin_db'
class Meta:
db_table = u'unit_tab'
def save(self, *args, **kwargs):
curr_time = int(time.time())
if not self.create_time:
self.create_time = curr_time
self.update_time = curr_time
super(UnitTab, self).save(*args, **kwargs)
def __unicode__(self):
return u'unit_tab_%s' % (self.unit_id)
And I am just saving the a UnitTab with UnitTab.objects.create() to create a new object. The unit_id has auto_increment, so I did not have to set it.
But if I use "u = UnitTab.objects.create()", the object "u" I get back is with unit_id as None — although the save is successful. So how can I can get the primary key(unit_id) of the UnitTab I just saved/created?
Edit: I am using Django 1.6.11 for my project
Edit: I logged those attributes and found that after u.save(), the unit_id is None. create_time is OK and the entity is saved successfully.
Edit: After changing from PositiveIntegerField to AutoField the unit_id field is auto assigned after save(). Just not sure why this is the case.
After you create your new object:
u = UnitTab.objects.create()
follow it up by refreshing its value from the db using the refresh_from_db() method.
u.refresh_from_db()
You can find this info here:
https://docs.djangoproject.com/en/1.9/ref/models/instances/#refreshing-objects-from-database
EDIT:
If you're on django 1.6, use the save() method and you should be able to access it like this:
>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id # Returns None, because b doesn't have an ID yet.
>>> b2.save()
>>> b2.id # Returns the ID of your new object.
https://docs.djangoproject.com/en/1.6/ref/models/instances/#auto-incrementing-primary-keys
Is there a particular reason you don't want to use models.AutoField instead?
unit_id = models.AutoField(primary_key=True)
I am working on a library system to manage certain items in our office, I don't need a full-blown integrated library system so I decided to hand roll one with Django.
Below is a simplified version of my model:
class ItemObjects(models.Model):
# Static Variables
IN_STATUS = 'Available'
OUT_STATUS = 'Checked out'
MISSING = 'Missing'
STATUS_CHOICES = (
(IN_STATUS, 'Available'),
(OUT_STATUS, 'Checked out'),
(MISSING, 'Missing'),
)
# Fields
slug = models.SlugField(unique=True)
date_added = models.DateField(auto_now_add=True)
last_checkin = models.DateTimeField(editable=False, null=True)
last_checkout = models.DateTimeField(editable=False, null=True)
last_activity = models.DateTimeField(editable=False, null=True)
status = models.CharField(choices=STATUS_CHOICES, default=IN_STATUS, max_length=25)
who_has = models.OneToOneField(User, blank=True, null=True)
times_out = models.PositiveIntegerField(default=0, editable=False)
notes = models.CharField(blank=True, max_length=500)
history = models.TextField(blank=True, editable=False)
pending_checkin = models.BooleanField(default=False)
pending_transfer = models.BooleanField(default=False)
At first I was using a method on ItemObject to process checking out an item to a user and who_has was an EmailField because I couldn't get a CharfField to populate with the logged in user's name, but I figured using a OneToOneField is probably closer to the "right" way to do this.. While who_has was an EmailField, the following method worked:
def check_out_itemobject(self, user):
user_profile = user.get_profile()
if self.status == 'Available' and self.who_has == '':
self.status = 'Checked out'
self.who_has = user.email
self.last_checkout = datetime.datetime.now()
self.last_activity = datetime.datetime.now()
self.times_out += 1
if self.history == '':
self.history += "%s" % user_profile.full_name
else:
self.history += ", %s" % user_profile.full_name
if user_profile.history == '':
user_profile.history += self.title
else:
user_profile.history += ", %s" % self.title
else:
return False # Not sure is this is "right"
user_profile.save()
super(ItemObjects, self).save()
Now that I am using a OneToOneField this doesn't work, so I started looking at using a subclass of ModelForm but none of the cases I saw here on SO seemed to apply for what I am trying to do; my form would be a button, and that's it. Here are some of the questions I looked at:
Django: saving multiple modelforms simultaneously (complex case)
(Django) (Foreign Key Issues) model.person_id May not be NULL
django update modelform
So was I on the right track with a sort of altered save() method, or would a ModelForm subclass be the way to go?
EDIT/UPDATE: Many thanks to #ChrisPratt!
So I am trying to get Chris Pratt's suggestion for showing ItemHistory to work, but when I try to render it on a page I get an AttributeError that states "'User' object has no attribute 'timestamp'". So my question is, why is it complaining about a User object when last_activity is an attribute on the ItemObject object ?
My view:
#login_required
def item_detail(request, slug):
item = get_object_or_404(Item, slug=slug)
i_history = item.last_activity
user = request.user
return render_to_response('items/item_detail.html',
{ 'item' : item,
'i_history': i_history,
'user' : user })
I do not see why a User object is coming up at this point.
EDIT2: Nevermind, history is clearly a M2M field whose target is User. That's why!
Assuming users will log in and check out books to themselves, then what you most likely want is a ForeignKey to User. A book will only have one User at any given time, but presumably Users could check out other items as well. If there is some limit, even if the limit is actually one per user, it would be better to validate this in the model's clean method. Something like:
def clean(self):
if self.who_has and self.who_has.itemobject_set.count() >= LIMIT:
raise ValidationError('You have already checked out your maximum amount of items.')
Now, you checkout method has a number of issues. First, status should be a defined set of choices, not just random strings.
class ItemObject(models.Model):
AVAILABLE = 1
CHECKED_OUT = 2
STATUS_CHOICES = (
(AVAILABLE, 'Available'),
(CHECKED_OUT, 'Checked Out'),
)
...
status = models.PositiveIntegerField(choices=STATUS_CHOICES, default=AVAILABLE)
Then, you can run your checks like:
if self.status == self.STATUS_AVAILABLE:
self.status = self.STATUS_CHECKED_OUT
You could use strings and a CharField instead if you like, as well. The key is to decouple the static text from your code, which allows much greater flexibility in your app going forward.
Next, history needs to be a ManyToManyField. Right now, your "history" is only who last checked the item out or what the last item the user checked out was, and as a result is pretty useless.
class ItemObject(models.Model):
...
history = models.ManyToManyField(User, through='ItemHistory', related_name='item_history', blank=True)
class ItemHistory(models.Model):
CHECKED_OUT = 1
RETURNED = 2
ACTIVITY_CHOICES = (
(CHECKED_OUT, 'Checked Out'),
(RETURNED, 'Returned'),
)
item = models.ForeignKey(ItemObject)
user = models.ForeignKey(User)
activity = models.PostiveIntegerField(choices=ACTIVITY_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-timestamp'] # latest first
Which then allows you to get full histories:
some_item.history.all()
some_user.item_history.all()
To add a new history, you would do:
ItemHistory.objects.create(item=some_item, user=some_user, activity=ItemHistory.CHECKED_OUT)
The auto_now_add attribute ensures that the timestamp is automatically set when the relationship is created.
You could then actually get rid of the last_checkout and last_activity fields entirely and use something like the following:
class ItemObject(models.Model):
...
def _last_checkout(self):
try:
return self.history.filter(activity=ItemHistory.CHECKED_OUT)[0].timestamp
except IndexError:
return None
last_checkout = property(_last_checkout)
def _last_activity(self):
try:
return self.history.all()[0].timestamp
except IndexError:
return None
last_activity = property(_last_activity)
And, you can then use them as normal:
some_item.last_checkout
Finally, your checkout method is not an override of save so it's not appropriate to call super(ItemObject, self).save(). Just use self.save() instead.