keyerror inside django model class __init__ - python

Here's a Django model class I wrote. This class gets a keyerror when I call get_object_or_404 from Django (I conceive that keyerror is raised due to no kwargs being passed to __init__ by the get function, arguments are all positional). Interestingly, it does not get an error when I call get_object_or_404 from console.
I wonder why, and if the below code is the correct way (ie, using init to populate the link field) to construct this class.
class Link(models.Model)
event_type = models.IntegerField(choices=EVENT_TYPES)
user = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add = True)
link = models.CharField(max_length=30)
isActive = models.BooleanField(default=True)
def _generate_link(self):
prelink = str(self.user.id)+str(self.event_type)+str(self.created_on)
m = md5.new()
m.update(prelink)
return m.hexdigest()
def __init__(self, *args, **kwargs):
self.user = kwargs['user'].pop()
self.event_type = kwargs['event_type'].pop()
self.link = self._generate_link()
super(Link,self).__init__(*args,**kwargs)

self.user = kwargs['user'].pop()
self.event_type = kwargs['event_type'].pop()
You're trying to retrieve an entry from the dictionary, and then call its pop method. If you want to remove and return an object from a dictionary, call dict.pop():
self.user = kwargs.pop('user')
Of course, this will fail with a KeyError when "user" is not present in kwargs. You'll want to provide a default value to pop:
self.user = kwargs.pop('user', None)
This means "if "user" is in the dictionary, remove and return it. Otherwise, return None".
Regarding the other two lines:
self.link = self._generate_link()
super(Link,self).__init__(*args,**kwargs)
super().__init__() will set link to something, probably None. I would reverse the lines, to something like this:
super(Link,self).__init__(*args,**kwargs)
self.link = self._generate_link()
You might want to add a test before setting the link, to see if it already exists (if self.link is not None: ...). That way, links you pass into the constructor won't be overwritten.

There's no reason to write your own __init__ for Django model classes. I think you'll be a lot happier without it.
Almost anything you think you want to do in __init__ can be better done in save.

I don't think you need the __init__ here at all.
You are always calculating the value of link when the class is instantiated. This means you ignore whatever is stored in the database. Since this is the case, why bother with a model field at all? You would be better making link a property, with the getter using the code from _generate_link.
#property
def link(self):
....

wonder why, and if the below code is the correct way (ie, using __init__ to populate the link field) to construct this class.
I once got some problems when I tried to overload __init__
In the maillist i got this answer
It's best not to overload it with your own
__init__. A better option is to hook into the post_init signal with a
custom method and in that method do your process() and
make_thumbnail() calls.
In your case the post_init-signal should do the trick and implementing __init__ shouldn't be necessary at all.
You could write something like this:
class Link(models.Model)
event_type = models.IntegerField(choices=EVENT_TYPES)
user = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add = True)
link = models.CharField(max_length=30)
isActive = models.BooleanField(default=True)
def create_link(self):
prelink = str(self.user.id)+str(self.event_type)+str(self.created_on)
m = md5.new()
m.update(prelink)
return m.hexdigest()
def post_link_init(sender, **kwargs):
kwargs['instance'].create_link()
post_init.connect(post_link_init, sender=Link)
>>> link = Link(event_type=1, user=aUser, created_on=datetime.now(), link='foo', isActive=True)
providing keyword unique for link = models.CharField(max_length=30, unique=True) could be helpful, too. If it is not provided, get_object_or_404 may won't work in case the same value in the link-field exists several times.
signals and unique in the django-docs

Related

what's the purpose of customizing django models

I read the following code for customizing Document model.
class DocumentQuerySet(models.QuerySet):
def pdfs(self):
return self.filter(file_type='pdf')
def smaller_than(self, size):
return self.filter(size__lt=size)
class DocumentManager(models.Manager):
def get_queryset(self):
return DocumentQuerySet(self.model, using=self._db) # Important!
def pdfs(self):
return self.get_queryset().pdfs()
def smaller_than(self, size):
return self.get_queryset().smaller_than(size)
class Document(models.Model):
name = models.CharField(max_length=30)
size = models.PositiveIntegerField(default=0)
file_type = models.CharField(max_length=10, blank=True)
objects = DocumentManager() #overriding the default model manager
Now suppose i want to retreive files of type pdf and size less than 1000.
Then i need to do the following:
Document.objects.pdfs().smaller_than(1000)
But what's the use of doing this even when i could have simply obtained the desired result by filtering the default model manager 'objects' using the following command:
Document.objects.filter(file_type='pdf', size__lt=1000)
What is the difference in the execution of above two commands?
Manager and Queryset methods are defined and used in order to use repeatable code.
If your use case only happens once, perhaps you are better off being explicit in your query, however if you repeat the exact same code elsewhere, perhaps you are better off making it a manager/queryset method.

Django Rest Framework: get_FOO_display in serializer not letting me POST

I recently added a source='get_fieldname_display to my serializer. It worked perfectly for the purpose of obtaining the display value of a choices tuple but now I can no longer POST data using the API end point without getting an error:
TypeError: 'get_fieldname_display' is an invalid keyword argument for this function
To be clear, the addition to the serializer was this line specifically:
fieldName = serializers.CharField(source='fieldName_display')
I know that this line is causing the problem because when I comment it out, I can POST data without a problem. However, I need this line in there so I may obtain the display names from the choices tuple when I am GETting data.
I think the problem may be remedied if I use two different serializers, one for GET and another for POST, but I am not sure how to go about doing this--I am using a generics.ListCreateAPIView in my views.py.
EDIT:
My model looks like this:
class MakeObjects(models.Model):
FIELD_NAME_CHOICES = (
("01", "Choice 1"),
("02", "Choice 2"),
)
fieldname = CharField(choices = FIELD_NAME_CHOICES)
My serializer looks like this:
class ObjectSerializer(serializers.ModelSerializer):
fieldname = serializers.CharField(source='get_fieldname_display')
class Meta:
model = MakeObjects
fields = ('__all__')
To achieve that, you need custom serializer field.
Here's the snippet for python 3:
class DisplayNameWritableField(serializers.ChoiceField):
def __init__(self, **kwargs):
self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff)
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
self.allow_blank = kwargs.pop('allow_blank', False)
super(ChoiceField, self).__init__(**kwargs)
def to_representation(self, value):
return self.choices.get(value, value)
def bind(self, field_name, parent):
super().bind(field_name, parent)
self.choices = parent.Meta.model._meta.get_field(field_name).choices
Then:
class YourModelSerializer(serializers.ModelSerializer):
your_choiced_model_field = DisplayNameWritableField()
This is a bit hacky though, so not all auto-docs engines detect choices correctly, even though this works pretty well.
You might also like https://github.com/encode/django-rest-framework/issues/1755
you can try adding the new field in:
read_only_fields = ['fieldName']

how to get datetime instance?

Well it is look like simple question. but i am in the learning stage can`t figure out.
This is my model class
class Store(models.Model):
file = models.FileField(upload_to=content_file_name)
file_name = models.CharField(max_length=100)
created_date = models.DateTimeField(auto_now_add=True)
userfild = models.ForeignKey(user,null=True, blank=True)
Ok for some clarification i just included my content_file_name function here
def content_file_name(instance, filename):
return os.path.join(
"user_%d" % instance.created_date, filename)
but the output of the folder structure looks like this
`None`
----filename
i can get the file_name instance like this --> intance.file_name
but when i enter instance.created_date it is return 'None' value.
What i am missing.? And my another doubt is i set it DateTimeField as auto_now_add=True So it`s saved into the db when form is submitted. But why None return?
auto_now_add is not populated until the object is saved. If you want a default that is applied as soon as the object is instantiated, use the default parameter with a callable:
created_date = models.DateTimeField(default=datetime.datetime.now)
Not quite sure that this is what you're asking, but here goes anyway:
You create an instance of any class by instantiating it:
Store()
In Python, this calls the class's init() function. Since you don't have one, it will use the 'constructor' from the class you're inheriting from, models.Model.
So, for example, you could do something like this:
my_store = Store()
my_store.file_name = 'some new name!'

Override defaults attributes of a Django form

In my Django app I have several different forms, which are similar in style. To not repeat myself over and over again, I try to rewrite the default form settings.
As a start I wanted to set some default settings for every form I use in my app and tried to subclass the django.forms.Form:
class DefaultForm(forms.Form):
error_css_class = 'alert'
error_class = DivErrorList
required_css_class = 'required'
label_suffix = ':'
auto_id = True
class TechnicalSurveyForm(DefaultForm):
location = forms.CharField(label='GPS Location')
satellite = forms.ModelChoiceField(queryset=get_satellites(), empty_label=None)
modem_sn = forms.CharField()
In my views.py I would call the Form simply with
tsurvey = TechnicalSurveyForm()
Unfortunately, the settings I set in DefaultForm are not in place (when I use TechnicalSurvey(auto_id = True, error_class = DivErrorList) they are). So, I guess my approach is totally wrong in some way. Can someone please help me out?
I guess the __init__ of forms.Form initializes attributes of a Form. You need to override the __init__ method and change attributes after Django has done its stuff.
EDIT: Indeed, after checking the django source code, you can see that attributes of a form object are initialized in the __init__ function. The method is visible on the github of django.
class DefaultForm(forms.Form):
def __init__(self, *args, **kwargs):
super(forms.Form, self ).__init__(*args, **kwargs)
self.error_css_class = 'alert'
self.error_class = DivErrorList
self.required_css_class = 'required'
self.label_suffix = ':'
self.auto_id = True
For Python beginners
This behavior is totally normal. Every attributes with the same name declared at the class declaration (as in the author example) will be override if it's also defined in the init function. There's a slightly difference between these two types of attributes declaration.

Django - Checking the type of Multi-table inheritence Querysets

I'm trying to hold a kind of table of contents structure in my database. Simplified example:
models.py
class Section (models.Model):
title = models.CharField(max_length=80)
order = models.IntegerField()
class SectionClickable(Section):
link = models.CharField(max_length=80)
class SectionHeading(Section):
background_color = models.CharField(max_length=6)
views.py
sections = Section.objects.filter(title="Hello!")
for section in sections:
if(section.sectionheading):
logger.debug("It's a heading")
I need to do some processing operations if it's a SectionHeading instance, but (as in the Django manual), accessing section.sectionheading will throw a DoesNotExist error if the object is not of type SectionHeading.
I've been looking into alternatives to this kind of problem, and I'm skimming over Generic Foreign Keys in the contenttypes package. However, this seems like it would cause even more headaches at the Django Admin side of things. Could anyone advise on a better solution than the one above?
Edit: I avoided abstract inheritence because of the order field. I would have to join the two QuerySets together and sort them by order
well you could check the type:
if isinstance(section, SectionHeading)
but duck typing is generally preferred
edit:
actually, that probably won't work. the object will be a Section. but you can look for the attribute:
if hasattr(section, 'sectionheading')
or
try:
do_something_with(section.sectionheading)
except AttributeError:
pass # i guess it wasn't one of those
The solution I came up using involved an extra field pointing to the (rather useful) ContentType class:
class Section(models.Model):
name = models.CharField(max_length=50)
content_type = models.ForeignKey(ContentType,editable=False,null=True)
def __unicode__(self):
try:
return self.as_leaf_class().__unicode__()
except:
return self.name
def save(self, *args, **kwargs):
if(not self.content_type):
self.content_type = ContentType.objects.get_for_model(self.__class__)
super(Section, self).save(*args, **kwargs)
def as_leaf_class(self):
content_type = self.content_type
model = content_type.model_class()
if(model == Section):
return self
return model.objects.get(id=self.id)
If you're going through "base" object, I think this solution is pretty nice and comfortable to work with.
I've been using something similar to what second suggests in his edit:
class SomeBaseModel(models.Model):
reverse_name_cache = models.CharField(_('relation cache'), max_length=10,
null=True, editable=False)
def get_reverse_instance(self):
try:
return getattr(self, self.reverse_name_cache)
except AttributeError:
for name in ['sectionclickable', 'sectionheading']:
try:
i = getattr(self, name)
self.reverse_name_cache = name
return i
except ObjectDoesNotExist:
pass
Now, this isn't exactly pretty, but it returns the subclass instance from a central place so I don't need to wrap other statements with try. Perhaps the hardcoding of subclass reverse manager names could be avoided but this approach was enough for my needs.
OP here.
While second's answer is correct for the question, I wanted to add that I believe multi-table inheritence is an inefficient approach for this scenario. Accessing the attribute of the sub-class model would cause a query to occur - thus requiring a query for every row returned. Ouch. As far as I can tell, select_related doesn't work for multi-table inheritence yet.
I also ruled out ContentTypes because it wouldn't do it elegantly enough and seemed to require a lot of queries also.
I settled on using an abstract class:
class Section (models.Model):
title = models.CharField(max_length=80)
order = models.IntegerField()
class Meta:
abstract=True
ordering=['order']
Queried both tables:
section_clickables = SectionClickable.objects.filter(video=video)
section_headings= SectionHeading.objects.filter(video=video)
and joined the two querysets together
#Join querysets http://stackoverflow.com/questions/431628/how-to-combine-2-or-more-querysets-in-a-django-view
s = sorted(chain(section_headings, section_clickables), key=attrgetter('order'))
Lastly I made a template tag to check the instance:
from my.models import SectionHeading, SectionClickable
#register.filter()
def is_instance(obj, c):
try:
return isinstance(obj, eval(c))
except:
raise ObjectDoesNotExist('Class supplied to is_instance could not be found. Import it in the template tag file.')
so that in my template (HamlPy) I could do this:
- if s|is_instance:"SectionClickable"
%span {{s.title}}
- if s|is_instance:"SectionHeading"
%span{'style':'color: #{{s.color}};'}
{{s.title}}
The result is that I only used two queries, one to get the SectionClickable objects and one for the SectionHeading objects

Categories