Using GenericForeignKeys Correctly - django

I'm trying to use generic foreign keys, but I can't seem to get them to work properly.
First, some context: I've got a messaging app and a groups app. Now, I want to be able to have players/groups write pms (private messages) to other users/groups. Here's my Pm model:
class Pm(models.Model):
"""Represents a private message (a la email) from one user to another."""
title = models.CharField(max_length=settings.max_title_length, default="(Blank)")
slug = models.SlugField(max_length=settings.max_title_length, editable=False)
#This was my code from when I only allowed pms to go from user to another
#user
#author = models.ForeignKey(Player, related_name="written_messages")
#recipient = models.ForeignKey(Player, related_name="recieved_messages")
text = models.TextField(max_length=settings.max_post_length)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
#Both will be either a group or a player
author = generic.GenericForeignKey('content_type', 'object_id')
recipient = generic.GenericForeignKey('content_type', 'object_id')
#[snip]
and here's the relevant bits of my Group and Player models:
class Group(models.Model):
#[snip]
written_messages = generic.GenericRelation("messaging.Pm")
sent_messages = generic.GenericRelation("messaging.Pm")
class Player(My_Model):
user = models.OneToOneField(User)
#[snip]
written_messages = generic.GenericRelation("messaging.Pm")
sent_messages = generic.GenericRelation("messaging.Pm")
Does this look correct?
When I run it, I get this traceback (so obviously something's wrong):
Traceback (most recent call last):
File "/usr/local/lib/python3.2/dist-packages/django/core/urlresolvers.py", line 339, in urlconf_module
return self._urlconf_module
AttributeError: 'RegexURLResolver' object has no attribute '_urlconf_module'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.2/wsgiref/handlers.py", line 137, in run
self.result = application(self.environ, self.start_response)
File "/usr/local/lib/python3.2/dist-packages/django/contrib/staticfiles/handlers.py", line 72, in __call__
return self.application(environ, start_response)
File "/usr/local/lib/python3.2/dist-packages/django/core/handlers/wsgi.py", line 180, in __call__
self.load_middleware()
File "/usr/local/lib/python3.2/dist-packages/django/core/handlers/base.py", line 49, in load_middleware
mw_instance = mw_class()
File "/usr/local/lib/python3.2/dist-packages/django/middleware/locale.py", line 24, in __init__
for url_pattern in get_resolver(None).url_patterns:
File "/usr/local/lib/python3.2/dist-packages/django/core/urlresolvers.py", line 346, in url_patterns
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
File "/usr/local/lib/python3.2/dist-packages/django/core/urlresolvers.py", line 341, in urlconf_module
self._urlconf_module = import_module(self.urlconf_name)
File "/usr/local/lib/python3.2/dist-packages/django/utils/importlib.py", line 35, in import_module
__import__(name)
File "/home/mark/Dropbox/Public/Galcon/galcon/galcon/urls.py", line 40, in <module>
("^messages/", include("messaging.urls")),
File "/usr/local/lib/python3.2/dist-packages/django/conf/urls/__init__.py", line 26, in include
urlconf_module = import_module(urlconf_module)
File "/usr/local/lib/python3.2/dist-packages/django/utils/importlib.py", line 35, in import_module
__import__(name)
File "/home/mark/Dropbox/Public/Galcon/galcon/messaging/urls.py", line 3, in <module>
from . import views
File "/home/mark/Dropbox/Public/Galcon/galcon/messaging/views.py", line 10, in <module>
from . import my_forms
File "/home/mark/Dropbox/Public/Galcon/galcon/messaging/my_forms.py", line 5, in <module>
class Modify_Message_Form(forms.ModelForm):
File "/usr/local/lib/python3.2/dist-packages/django/forms/models.py", line 283, in __new__
raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) (recipient) specified for Pm
The mentioned form is pretty simple:
class Modify_Message_Form(forms.ModelForm):
class Meta:
model = Pm
fields = ["title", "recipient", "text"]
What have I done wrong? Thanks!

Using the name of the GenericForeignKey in the form doesn't work as it's not actually a real field but more of a convenience. There's no widget to display the relationship; you usually display the content_type and object_id. If you want to see the relationship in the admin interface then I'd recommend looking at Grappelli.
You also need content_type and object_id fields for each GenericForeignKey.
author_content_type = models.ForeignKey(ContentType)
author_object_id = models.PositiveIntegerField()
recipient_content_type = models.ForeignKey(ContentType)
recipient_object_id = models.PositiveIntegerField()
author = generic.GenericForeignKey('author_content_type', 'author_object_id')
recipient = generic.GenericForeignKey('recipient_content_type', 'recipient_object_id')
I've not much experience with GenericRelations but from what I know you'd also need to specify the content_type and object_id fields in your Player and Group models.
written_messages = generic.GenericRelation(messaging.Pm
content_type_field='author_content_type',
object_id_field='author_object_id')

It seems, by looking at the traceback, that you cannot use GenericForeignKeys as form fields. But I think you can use recipient_content_type and recipient_content_id instead, which is what Django admin usually shows to users.

Related

Django Sessions, handling of datetime types

I'm using sessions in a current django project and recently got a 'Object of type 'date' is not JSON serializable' error - due to the move_in_date field below.
When saving a modelform of the below model to the session via:
if form.is_valid():
request.session.update(form.cleaned_data)
my model:
class Address(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
house_name_number = models.CharField(max_length=255, verbose_name="house name or number")
street_name = models.CharField(max_length=255)
town_city = models.CharField(max_length=255)
county = models.CharField(max_length=255)
postcode = models.CharField(max_length=8)
same_address = models.BooleanField()
move_in_date = models.DateField(null=True, blank=True)
I've tried to solve the issue by using DjangoJSONEncoder as suggested by the docs, which can handle datetimes via the settings with SESSION_SERIALIZER=DjangoJSONEncoder (should this be a serializer rather than an encoder?), but trying that or SESSION_SERIALIZER=PickleSerializer both give an Attribute error - ... has no attribute 'rsplit'
Additionally I was using django wizard before which stores intermediate data (such as the field causing the date issue above) in the session. I've now switched that part of the app to use seperate views for flexibility (as signup wasn't just a linear path), django wizard doesn't have this issue, how does it get round this?
Updated with stacktrace
Traceback (most recent call last):
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/utils/deprecation.py", line 142, in __call__
response = self.process_response(request, response)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/middleware.py", line 58, in process_response
request.session.save()
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/backends/db.py", line 83, in save
obj = self.create_model_instance(data)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/backends/db.py", line 69, in create_model_instance
session_data=self.encode(data),
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/backends/base.py", line 98, in encode
serialized = self.serializer().dumps(session_dict)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/core/signing.py", line 93, in dumps
return json.dumps(obj, separators=(',', ':')).encode('latin-1')
File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 238, in dumps
**kw).encode(obj)
File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 180, in default
o.__class__.__name__)
TypeError: Object of type 'date' is not JSON serializable
Stack trace for trying DjangoJSONEncoder:
Traceback (most recent call last):
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/utils/deprecation.py", line 138, in __call__
response = self.process_request(request)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/middleware.py", line 20, in process_request
request.session = self.SessionStore(session_key)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/backends/db.py", line 18, in __init__
super(SessionStore, self).__init__(session_key)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/contrib/sessions/backends/base.py", line 51, in __init__
self.serializer = import_string(settings.SESSION_SERIALIZER)
File "/Users/Barclay/.virtualenvs/switcher5/lib/python3.6/site-packages/django/utils/module_loading.py", line 15, in import_string
module_path, class_name = dotted_path.rsplit('.', 1)
AttributeError: type object 'DjangoJSONEncoder' has no attribute 'rsplit'
A few things of confusion are hitting you:
When setting the serializer, do not provide a class reference, but a dotted part. This is seen in the error:
module_path, class_name = dotted_path.rsplit('.', 1)
DjangoJSONEncoder isn't the right fix for a serializer. It is referenced in the documentation as a way to serialize models before putting them into the session.
If you want to make a smart serializer then you still need to create a Serializer, which should support a dumps and loads interface, that leverage a JsonEncoder and JsonDecoder respectively.
The Pickle serializer will work just fine, but as said you need to provide the dotted path.
If you want to use JSON as serializer, then this might be a good start:
from django.core.serializers.json import DjangoJSONEncoder
from django.core.signing import JSONSerializer as BaseJSONSerializer
class SmartJSONSerializer(BaseJSONSerializer):
def dumps(self, obj):
return json.dumps(obj, separators=(',', ':'), cls=DjangoJSONEncoder).encode('latin-1')

I am getting a SyntaxError in my model.py where could be the issue with my code

iam writing my code in model.py but this error that i getting in my development server.Any suggestions are welcome.I have followed previous suggections in stackflow but i can't get any answer that is solving my code.
Error:the code below is the error i am getting.
Unhandled exception in thread started by <function wrapper at 0x7fe6d63e4938>
Traceback (most recent call last):
File "/usr/lib64/python2.7/site-packages/django/utils/autoreload.py", line 227, in wrapper
fn(*args, **kwargs)
File "/usr/lib64/python2.7/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run
autoreload.raise_last_exception()
File "/usr/lib64/python2.7/site-packages/django/utils/autoreload.py", line 250, in raise_last_exception
six.reraise(*_exception)
File "/usr/lib64/python2.7/site-packages/django/utils/autoreload.py", line 227, in wrapper
fn(*args, **kwargs)
File "/usr/lib64/python2.7/site-packages/django/__init__.py", line 27, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/lib64/python2.7/site-packages/django/apps/registry.py", line 108, in populate
app_config.import_models()
File "/usr/lib64/python2.7/site-packages/django/apps/config.py", line 202, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib64/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
File "/home/harrugg2/projects/django/church/tithe/models.py", line 24
def print(self):
^
SyntaxError: invalid syntax
below is my code for model.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.utils import timezone
from django.db import models
# Create your models here.
class tithe(models.models):
genarationNo = models(ForeignKey('generationNo.User'))
memberid = models.IntField()
membername = models.VacharField()
tithe = models.IntField()
combinedoffering = models.IntField(max_length=45)
campmeetingoffering = models.IntField(IntField=45)
churchbuilding = models.IntField(max_length=45)
conference = models.IntField(max_length=45)
localchurch = models.IntField(max_length=45)
funds = models.IntField(max_length=45)
total = models.IntField(max_length=45)
created_date = models.DateTimeField(
default=timezone.now)
printed_date = models.DateTimeField(
blank=True, null=True)
def print(self):
self.printed_date = timezone.now()
self.save()
def __str__(self):
return self.generationNo
class Meta:
unique_together = ["generationNo","IntField"]
ordering = ["printed_date","membername"]
print is a keyword of the language. It is reserved and can not be used as an identifier. See this.
As others said, print is not a good name for a function but there is something else strange with your code.
In unique_together you are referencing a field that does not exist.

Django reversed OneToOne relation with select_related

I have below model and I want to perform below query:
Post.objects.select_related(
'previous_post', 'next_post'
).get(id=some_id)
# models.py
class Post(models.Model):
title = models.CharField(max_length=60, unique=True)
description = models.TextField()
content = models.TextField()
previous_post = models.OneToOneField('self', null=True, blank=True,
related_name='next_post',
on_delete=models.PROTECT)
For some reason it does not work with next_post parameter, as I get following error:
raise IndexError("Number of args exceeds number of fields")
IndexError: Number of args exceeds number of fields
Theoretically I can live without select_related, but I would prefer not to give up it in this case and I am really curious whether I am doing something wrong or this is just a Django bug.
Full traceback:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/query.py", line 381, in get
num = len(clone)
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/query.py", line 240, in __len__
self._fetch_all()
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/query.py", line 1074, in _fetch_all
self._result_cache = list(self.iterator())
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/query.py", line 72, in __iter__
rel_populator.populate(row, obj)
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/query.py", line 1715, in populate
obj = self.model_cls.from_db(self.db, self.init_list, obj_data)
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/base.py", line 460, in from_db
new = cls(*values)
File "/home/konrad/PycharmProjects/jdg/env/lib/python3.4/site-packages/django/db/models/base.py", line 372, in __init__
raise IndexError("Number of args exceeds number of fields")
IndexError: Number of args exceeds number of fields
It looks like a bug in Django. I can reproduce in 1.8 and 1.9, but not in the master branch.
Doing a git bisect, tt appears to have been fixed by ticket 26207, so it should be fixed in Django 1.10.

get an error while applying a widget to a field

Here is my model
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
serving_size = models.ForeignKey(ServingSize)
quantity = models.IntegerField()
order = models.IntegerField()
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
and my model form
class RecipeIngredientForm(forms.ModelForm):
recipe_ingredient = forms.CharField()
class Meta:
model = RecipeIngredient
widgets = {
'serving_size' : forms.Select(attrs={'class' : 'test'}),
'recipe_ingredient' : forms.TextInput(),
}
I get the following error when I navigate to the page
Unhandled exception in thread started by <function inner_run at 0x1010faf50>
Traceback (most recent call last):
File "/Library/Python/2.6/site-packages/django/core/management/commands/runserver.py", line 48, in inner_run
self.validate(display_num_errors=True)
File "/Library/Python/2.6/site-packages/django/core/management/base.py", line 245, in validate
num_errors = get_validation_errors(s, app)
File "/Library/Python/2.6/site-packages/django/core/management/validation.py", line 28, in get_validation_errors
for (app_name, error) in get_app_errors().items():
File "/Library/Python/2.6/site-packages/django/db/models/loading.py", line 146, in get_app_errors
self._populate()
File "/Library/Python/2.6/site-packages/django/db/models/loading.py", line 61, in _populate
self.load_app(app_name, True)
File "/Library/Python/2.6/site-packages/django/db/models/loading.py", line 78, in load_app
models = import_module('.models', app_name)
File "/Library/Python/2.6/site-packages/django/utils/importlib.py", line 35, in import_module
__import__(name)
File "/models.py", line 128, in <module>
RecipeIngredientFormSet = inlineformset_factory(Recipe, RecipeIngredient, extra=1, form=RecipeIngredientForm)
File "/Library/Python/2.6/site-packages/django/forms/models.py", line 838, in inlineformset_factory
FormSet = modelformset_factory(model, **kwargs)
File "/Library/Python/2.6/site-packages/django/forms/models.py", line 669, in modelformset_factory
formfield_callback=formfield_callback)
File "/Library/Python/2.6/site-packages/django/forms/models.py", line 407, in modelform_factory
return ModelFormMetaclass(class_name, (form,), form_class_attrs)
File "/Library/Python/2.6/site-packages/django/forms/models.py", line 220, in __new__
opts.exclude, opts.widgets, formfield_callback)
File "/Library/Python/2.6/site-packages/django/forms/models.py", line 178, in fields_for_model
formfield = formfield_callback(f, **kwargs)
TypeError: <lambda>() got an unexpected keyword argument 'widget'
but if I remove this line from my RecipeIngredientForm the error goes away
'serving_size' : forms.Select(attrs={'class' : 'test'}),
Any ideas whatI did wrong?
Just don't add Select widget to this field, it's ModelChoiceField, and is rendered as select box anyway. If you put Select widget on ModelChoiceField, it won't render properly (will show unicode output in value rather than ID).
Also you don't need the first CharField unless you want a text input with auto-completion.
To show the input element with custom attributes, use {% field %} tag:
{% field myform.myselect class="XYZ" %}
renders it to
<select name="..." class="XYZ">...
I think you'll agree that it's a bad idea to customize classes and templates inside forms or models. It makes it impossible to debug templates.
I ran into a similar problem.
I specified a custom "form" kwarg in a modelformset_factory constructor. This would consistently fail with the error you describe while that form class had custom widgets specified in its meta class:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'myField' : forms.fields.TextInput(attrs={'readonly':'readonly'}),
}
MyFormFactory = modelformset_factory(MyModel,form=MyForm)
Changing the form class to specify custom widgets in the init method seemed to solve this:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self,*args,**kwargs):
self.fields['myField'].widget = forms.fields.TextInput(attrs={'readonly':'readonly'})

Django generic relation field reports that all() is getting unexpected keyword argument when no args are passed

I have a model which can be attached to to other models.
class Attachable(models.Model):
content_type = models.ForeignKey(ContentType)
object_pk = models.TextField()
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
class Meta:
abstract = True
class Flag(Attachable):
user = models.ForeignKey(User)
flag = models.SlugField()
timestamp = models.DateTimeField()
I'm creating a generic relationship to this model in another model.
flags = generic.GenericRelation(Flag)
I try to get objects from this generic relation like so:
self.flags.all()
This results in the following exception:
>>> obj.flags.all()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python2.6/dist-packages/django/db/models/manager.py", line 105, in all
return self.get_query_set()
File "/usr/local/lib/python2.6/dist-packages/django/contrib/contenttypes/generic.py", line 252, in get_query_set
return superclass.get_query_set(self).filter(**query)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py", line 498, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py", line 516, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 1675, in add_q
can_reuse=used_aliases)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 1569, in add_filter
negate=negate, process_extras=process_extras)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 1737, in setup_joins
"Choices are: %s" % (name, ", ".join(names)))
FieldError: Cannot resolve keyword 'object_id' into field. Choices are: content_type, flag, id, nestablecomment, object_pk, timestamp, user
>>> obj.flags.all(object_pk=obj.pk)
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: all() got an unexpected keyword argument 'object_pk'
What have I done wrong?
You need to define object_id_field and content_type_field when creating GenericRelation:
flags = generic.GenericRelation(Flag, object_id_field="object_pk", content_type_field="content_type")