'RelatedObject' object has no attribute 'unique'

157 views
Skip to first unread message

Thiago F. Crepaldi

unread,
Jan 19, 2009, 7:50:34 AM1/19/09
to Django users
Hello,

I am migrating a system from django 0.97 (svn) to 1.0.2 (stable) and I
got the following error when a [database] SEARCH FORM is submitted :
'RelatedObject' object has no attribute 'unique'

I recreated all tables through 'python manage.db syncdb' command and
imported all the old data back again.

It looks like that all unique indexes declared were created correctly
on the MySQL database.

In fact, I didn't even understand what this error means. Can someone
help me out ?

I already read
http://docs.djangoproject.com/en/dev/releases/1.0-porting-guide/
and
http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges
and, unfortunately, had no luck =/


Environment:

Request Method: POST
Request URL: http://localhost:8000/ftrTool/searchFeature/
Django Version: 1.0.2 final
Python Version: 2.5.2
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.admin',
'webTool.ftrTool']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.doc.XViewMiddleware')


Traceback:
File "C:\PYTHON25\Lib\site-packages\django\core\handlers\base.py" in
get_response
86. response = callback(request, *callback_args,
**callback_kwargs)
File "D:\thiago\dados\eclipse_workspace\webTool\..\webTool\ftrTool
\views.py" in searchFeature
474. if form.is_valid():
File "C:\PYTHON25\Lib\site-packages\django\forms\forms.py" in is_valid
120. return self.is_bound and not bool(self.errors)
File "C:\PYTHON25\Lib\site-packages\django\forms\forms.py" in
_get_errors
111. self.full_clean()
File "C:\PYTHON25\Lib\site-packages\django\forms\forms.py" in
full_clean
241. self.cleaned_data = self.clean()
File "C:\PYTHON25\Lib\site-packages\django\forms\models.py" in clean
223. self.validate_unique()
File "C:\PYTHON25\Lib\site-packages\django\forms\models.py" in
validate_unique
251. if f.unique and self.cleaned_data.get(name) is not
None:

Exception Type: AttributeError at /ftrTool/searchFeature/
Exception Value: 'RelatedObject' object has no attribute 'unique'


--
Thiago
Message has been deleted

Karen Tracey

unread,
Jan 19, 2009, 9:37:34 AM1/19/09
to django...@googlegroups.com
On Mon, Jan 19, 2009 at 7:50 AM, Thiago F. Crepaldi <tog...@gmail.com> wrote:

Hello,

I am migrating a system from django 0.97 (svn) to 1.0.2 (stable) and I
got the following error when a [database] SEARCH FORM is submitted :
'RelatedObject' object has no attribute 'unique'

I recreated all tables through 'python manage.db syncdb' command and
imported all the old data back again.

Just FYI, this is not necessary to migrate from .9x to 1.0.2.  Tables from .9x installations work perfectly well with 1.0.x.
 

It looks like that all unique indexes declared were created correctly
on the MySQL database.

In fact, I didn't even understand what this error means. Can someone
help me out ?

It would be easier to help if you shared the code you have in your ftrTool\views.py searchFeature function, as well as the form definition you are using.  All I can say from the traceback below is that the unique validation code is finding it's got a RelatedObject object where it is expecting to have something else.  Whether that is a bug in your code or in the unique validation code is hard to say without seeing your code.

Karen
 

Thiago F. Crepaldi

unread,
Jan 19, 2009, 12:04:11 PM1/19/09
to Django users
hello Karen,

below is the code required. I hope you can help me =)


thanks in advance
Thiago

here is the FEATURE MODEL unique property
#
# Fields that should be unique together in the database.
#
unique_together = [("featureId", "pf", "intComponent", "db",
"pd")]


here is the def searchFeature in the views.py
#*****************************************************************************************************************************************************************
# searchFeature
#*****************************************************************************************************************************************************************
def searchFeature(request, view = None):
if request.method == 'POST':
form = SearchFeatureForm(request.POST)
if form.is_valid():
# Perform the query

ftrQuery = Feature.objects.all().order_by('featureId',
'pk')

# Remove empty fields and start filtering
if request.POST.__getitem__('featureId') != "":
ftrQuery = ftrQuery.filter(featureId__contains =
request.POST.__getitem__('featureId')).distinct()

if request.POST.__getitem__('name') != "":
ftrQuery = ftrQuery.filter(name__icontains =
request.POST.__getitem__('name')).distinct()

if request.POST.__getitem__('pf') != "":
ftrQuery = ftrQuery.filter(platform =
request.POST.__getitem__('pf')).distinct()

if request.POST.__getitem__('state') != "":
ftrQuery = ftrQuery.filter(state =
request.POST.__getitem__('state')).distinct()

if request.POST.__getitem__('db') != "":
ftrQuery = ftrQuery.filter(drumbeat__pk =
request.POST.__getitem__('db')).distinct()

if request.POST.__getitem__('intComponent') != "":
ftrQuery = ftrQuery.filter(intComponent =
request.POST.__getitem__('intComponent')).distinct()

if request.POST.__getitem__('estimator') != "":
ftrQuery = ftrQuery.filter
(estimate__estimator__name__icontains = request.POST.__getitem__
('estimator')).distinct()

if request.POST.__getitem__('ftrStartYear') != "":
ftrStartYear = int(request.POST.__getitem__
('ftrStartYear'))

if request.POST.__getitem__('ftrStartMonth') != "":
ftrStartMonth = int(request.POST.__getitem__
('ftrStartMonth'))

if request.POST.__getitem__('owner') != "":
ftrQuery = ftrQuery.filter(owner__pk =
request.POST.__getitem__('owner'))

if request.POST.__getitem__('pd') != "":
ftrQuery = ftrQuery.filter(product__contains =
request.POST.__getitem__('pd'))

from datetime import date
if request.POST.__getitem__('ftrStartYear') != "" and
request.POST.__getitem__('ftrStartMonth') != "":
ftrQuery = ftrQuery.filter(date__gte = date
(ftrStartYear, ftrStartMonth, 1)).distinct()

if request.POST.__getitem__('teamMember') != "":
role = request.POST.__getitem__('memberRole')
if role == "ANY":
role = ""
ftrQuery = ftrQuery.filter(Q
(teammember__employee__name__icontains = request.POST.__getitem__
('teamMember')) & Q(teammember__role__contains = role)).distinct()

actualEffortLow = 0
actualEffortHigh = 0
if request.POST.__getitem__('actualEffortLow') != '':
actualEffortLow = int(request.POST.__getitem__
('actualEffortLow'))

if request.POST.__getitem__('actualEffortHigh') != '':
actualEffortHigh = int(request.POST.__getitem__
('actualEffortHigh'))

if request.POST.__getitem__('actualEffortLow') != '' or
request.POST.__getitem__('actualEffortHigh') != '':
ftrQuery = ftrQuery.filter(Q(actual__type__contains=
'effort') & Q(actual__actual__gte = actualEffortLow) & Q
(actual__actual__lte = actualEffortHigh)).distinct()

actualSizeLow = 0
actualSizeHigh = 0
if request.POST.__getitem__('actualSizeLow') != '':
actualSizeLow = int(request.POST.__getitem__
('actualSizeLow'))

if request.POST.__getitem__('actualSizeHigh') != '':
actualSizeHigh = int(request.POST.__getitem__
('actualSizeHigh'))

if request.POST.__getitem__('actualSizeLow') != '' or
request.POST.__getitem__('actualSizeHigh') != '':
ftrQuery = ftrQuery.filter(Q
(actual__type__contains='size') & Q(actual__actual__gte =
request.POST.__getitem__('actualSizeLow')) & Q(actual__actual__lte =
request.POST.__getitem__('actualSizeHigh'))).distinct()

queryTeamSizeLow = request.POST.__getitem__('teamSizeLow')
queryTeamSizeHigh = request.POST.__getitem__
('teamSizeHigh')

#Only way I could find to select with COUNT without
writting the SQL code.
if queryTeamSizeLow != '':
for item in ftrQuery:
if item.teammember_set.count() < int
(queryTeamSizeLow):
ftrQuery = ftrQuery.exclude
(featureId=item.featureId)

if queryTeamSizeHigh != '':
for item in ftrQuery:
if item.teammember_set.count() > int
(queryTeamSizeHigh):
ftrQuery = ftrQuery.exclude
(featureId=item.featureId)

queryNumDepsLow = request.POST.__getitem__('numDepsLow')
queryNumDepsHigh = request.POST.__getitem__('numDepsHigh')

if queryNumDepsLow != '':
for item in ftrQuery:
if item.dependency_set.count() < int
(queryNumDepsLow):
ftrQuery = ftrQuery.exclude
(featureId=item.featureId)

if queryNumDepsHigh != '':
for item in ftrQuery:
if item.dependency_set.count() > int
(queryNumDepsHigh):
ftrQuery = ftrQuery.exclude
(featureId=item.featureId)

if request.POST.__getitem__('dependency') != "":
for item in ftrQuery:
if item.dependency_set.filter
(dependency__name__contains = request.POST.__getitem__
('dependency')).count() == 0:
ftrQuery = ftrQuery.exclude
(featureId=item.featureId)

return render_to_response('ftrTool/searchFtr.html',
{'view':view, 'form': form, 'ftrQuery': ftrQuery},
context_instance=RequestContext
(request))
else:
form = SearchFeatureForm()
return render_to_response('ftrTool/searchFtr.html', {'view':view,
'form': form},
context_instance=RequestContext(request))

Here is the form definition
#*****************************************************************************************************************************************************************
# Search Feature Form.
#*****************************************************************************************************************************************************************
class SearchFeatureForm(forms.ModelForm):

# Default parameters, overriding required attribute to false.
featureId = forms.IntegerField(required = False,
widget=forms.TextInput(attrs={'size':'5'}))
name = forms.CharField(required = False)
platform = forms.ChoiceField(required = False, choices =
FORM_QUERY_PLATFORM_CHOICES)
drumbeat = forms.ModelChoiceField(Drumbeat.objects.all(),
required = False)
intComponent = forms.ChoiceField(required = False, choices =
FORM_QUERY_COMPONENT_CHOICES)
product = forms.CharField(required = False)
owner = forms.ModelChoiceField(Component.objects.all(),
required = False)
devType = forms.ChoiceField(label = "Development Type",
required = False, choices = FORM_QUERY_DEV_TYPE_CHOICES)
state = forms.ChoiceField(required = False, choices =
FORM_QUERY_STATE_CHOICES)

#Extra Parameters For Query
actualSizeLow = forms.IntegerField(label="Actual Size",
required = False, widget=forms.TextInput(attrs={'size':'3'}))
actualSizeHigh = forms.IntegerField(label="Actual Size",
required = False, widget=forms.TextInput(attrs={'size':'3'}))
actualEffortLow = forms.IntegerField(label="Actual Effort",
required = False, widget=forms.TextInput(attrs={'size':'3'}))
actualEffortHigh = forms.IntegerField(label="Actual Effort",
required = False, widget=forms.TextInput(attrs={'size':'3'}))
teamMember = forms.CharField(label="Team Member", required =
False, widget=forms.TextInput(attrs={'size':'25'}))
memberRole = forms.ChoiceField(label = 'As', choices =
FORM_QUERY_ROLE_CHOICES, required = False)
estimator = forms.CharField(label="Estimator", required =
False)
teamSizeLow = forms.IntegerField(label="Team Size", required
= False, widget=forms.TextInput(attrs={'size':'3'}))
teamSizeHigh = forms.IntegerField(label="Team Size", required
= False, widget=forms.TextInput(attrs={'size':'3'}))
#** ftrStartYear = forms.IntegerField(label="Year", required =
False, max_value=2100, min_value=2000, widget=forms.TextInput(attrs=
{'size':'5', 'maxlength':'4'}))
#** ftrStartMonth = forms.IntegerField(label="Month", required =
False, max_value=12, min_value=1, widget=forms.TextInput(attrs=
{'size':'2', 'maxlength':'2'}))
ftrStartYear = forms.IntegerField(label="Year", required =
False, max_value=2100, min_value=2000, widget=forms.TextInput(attrs=
{'size':'5', 'max_length':'4'}))
ftrStartMonth = forms.IntegerField(label="Month", required =
False, max_value=12, min_value=1, widget=forms.TextInput(attrs=
{'size':'2', 'max_length':'2'}))
numDepsLow = forms.IntegerField(label="Number of
Dependencies", required = False, widget=forms.TextInput(attrs=
{'size':'3'}))
numDepsHigh = forms.IntegerField(label="Number of
Dependencies", required = False, widget=forms.TextInput(attrs=
{'size':'3'}))
dependency = forms.CharField(label = "Dependency", required
= False, widget=forms.TextInput(attrs={'size':'25'}))

class Meta:
model = Feature
exclude = ('date','description', 'wikiLink')

On Jan 19, 12:37 pm, "Karen Tracey" <kmtra...@gmail.com> wrote:

Karen Tracey

unread,
Jan 19, 2009, 1:54:59 PM1/19/09
to django...@googlegroups.com
On Mon, Jan 19, 2009 at 12:04 PM, Thiago F. Crepaldi <tog...@gmail.com> wrote:

hello Karen,

below is the code required. I hope you can help me =)

In the future, please post code of that length/width to someplace like dpaste.com; email makes it quite hard to read given how it gets wrapped.

I think what is happening is you have added a field to your model form that has a name that matches a related object accessor for the model (that is, you have added a field with a name that matches that of a model that has a ForeignKey that points to your Feature model).  I've recreated the error with a much simpler model form based on the tutorial's Poll model:

http://code.djangoproject.com/ticket/10069

I thought the fix was easy but a quick run of Django's model_forms test shows that the quick fix I was thinking of breaks something else, and I'm running out of time to look at this today, so it may be a little while before a fix goes in.  In the meantime if you can identify the additional field(s) that may be causing the problem in your form, renaming them to something that doesn't exactly match the related model name(s) should work as a temporary workaround.

Karen

Karen Tracey

unread,
Jan 19, 2009, 2:45:17 PM1/19/09
to django...@googlegroups.com

Actually the fix was simple,  I just didn't spell it properly first time around.   Fix is now in trunk and current 1.0.X branch so if you update to one of those levels I think the problem will go away.

Karen

bruno desthuilliers

unread,
Jan 19, 2009, 4:18:19 PM1/19/09
to Django users
On 19 jan, 18:04, "Thiago F. Crepaldi" <togn...@gmail.com> wrote:

Absolutely not an answer to your question, but...

> below is the code required. I hope you can help me =)
>
#*****************************************************************************************************************************************************************
> # searchFeature

#*****************************************************************************************************************************************************************
> def searchFeature(request, view = None):
> if request.method == 'POST':
> form = SearchFeatureForm(request.POST)
> if form.is_valid():
> # Perform the query
>
> ftrQuery = Feature.objects.all().order_by('featureId',
> 'pk')

Since your going to filter this out, starting by querying all is
useless (thanks to querysets being lazy, it's at least _only_ useless.

Remember that Django has Q objects for building complex queries, and
that you can just collect Q objects in a list, then "or" or (like in
this case) "and" them together in just one pass.

>
> # Remove empty fields and start filtering
> if request.POST.__getitem__('featureId') != "":

First point : you should never directly access __magic_methods__ in
Python - they are here as implementation for operators. In this case,
you want:

if request.POST.get('featureId', '') != '':

Second point: since empty strings eval to false in a boolean context,
you don't have to explicitely compare them with the empty string. IOW:

if request.POST.get('featureId, ''):

> ftrQuery = ftrQuery.filter(featureId__contains =
> request.POST.__getitem__('featureId')).distinct()


Third point : you're doing the same access twice. Better to do it just
once:

featureId = request.POST.get('featureId, '')
if featureId:
# code here

While we're at it, attribute lookup is a somewhat costly operation in
Python, specially when the attribute resolves to a method. So if
you're going to heavily use the same method of the same object, it's
better to alias it to a local name:

get_from_post = request.POST.get

featureId = get_from_post('featureId, '')
if featureId:
# code here


While we're at it : your form object is supposed to give you access to
the validated (and possibly converted) data extracted from the
request. Is there any reason to not use this feature ?

Next point : you can pass dict objects to Queryset.filter(), (or to Q
objects) using the ** notation (cf the section of the official Python
tutorial about functions and named / keyword arguments). So you could
greatly simplify you code:

filters = [
# fieldname, lookup
("featureId", "featureId__contains"),
("name", "name__icontains")
("pf", "platform"),
("state", "state"),
("db", "drumbeat__pk"),
# etc
]

queries = []
add_q = queries.append
for fieldname, lookup in filters:
value = get_from_post(fieldname, '')
if value:
add_q(Q(**{lookup:value}))


# now "and" the Q objects:
filters = reduce(lambda q1, q1 : q1 & q2, filters)

# and do the query:
if filters:
queryset = Feature.objects.filter(filters)
else:
queryset = Feature.objects.all()

queryset = queryset.order_by('featureId', 'pk')

This can not apply to all your query construction (there are some
complex cases in it), but it should already help uncluttering it for
all the simple cases.

(snip)

> from datetime import date

Better to put import statements at the module's top-level.

> #Only way I could find to select with COUNT without
> writting the SQL code.

You might have a look at the example snippet for the 'extra' method of
querysets.



> if queryTeamSizeLow != '':
> for item in ftrQuery:
> if item.teammember_set.count() < int
> (queryTeamSizeLow):

Oh my. Pity this poor computer, and at least convert queryTeamSizeLow
to an int only once...


> ftrQuery = ftrQuery.exclude
> (featureId=item.featureId)
>
> if queryTeamSizeHigh != '':
> for item in ftrQuery:
> if item.teammember_set.count() > int
> (queryTeamSizeHigh):
> ftrQuery = ftrQuery.exclude
> (featureId=item.featureId)


Ok. Some things are better done building raw sql queries. Django's ORM
is here to help with the easy and boring 80%, not to replace SQL.

My 2 cents...

(snip)

Thiago F. Crepaldi

unread,
Jan 20, 2009, 6:28:05 AM1/20/09
to Django users
Hello Karen,

First of all, I really would like to thank you for helping me solve
this issue. Your fix (downloaded from http://code.djangoproject.com/changeset/9778?format=zip&new=9778)
was perfect and my form is working again, but now with a final release
(1.0.2) instead of a development one (svn 0.97)

I am happy django community have smart and helpful developers to make
this framework better everyday

Att
Thiago

On Jan 19, 5:45 pm, "Karen Tracey" <kmtra...@gmail.com> wrote:
> On Mon, Jan 19, 2009 at 1:54 PM, Karen Tracey <kmtra...@gmail.com> wrote:

Thiago F. Crepaldi

unread,
Jan 20, 2009, 6:29:21 AM1/20/09
to Django users
Hello Bruno,

Karen Tracey solved my issue and now i will be able to apply those
refactoring you suggested me.

Thanks for tips :-)

Att
Thiago

On Jan 19, 7:18 pm, bruno desthuilliers
Reply all
Reply to author
Forward
0 new messages