Discussion:
How to make a foreign key field of type other than int?
Kayode Odeyemi
2011-10-24 10:14:59 UTC
Permalink
Hello all,

Is there a way to have a foreign key on a model which uses a type other than
Django's forceful
int type to make the storage for a ModelChoiceField widget?

I have something like this I'm working with:

forms.py
------------

class BranchModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj

class TransactionUpdateForm(forms.ModelForm):
branchqs = Branch.objects.values_list(u'name_branch', flat=True) # use
flat=True if values_list is called with a single field
branch_name = BranchModelChoiceField(branchqs)
class Meta:
model = Transaction
fields = ('teller_no', 'paid')

models.py
--------------
class Branch(models.Model):
""" Branch """
bid = models.AutoField(primary_key=True)
name_branch = models.CharField(max_length=255)

class Meta:
db_table = u'branch'

def __unicode__(self):
return self.name_branch

class Transaction(models.Model):
id = models.AutoField(primary_key=True)
branch_name = models.ForeignKey(Branch, blank=True, null=True,
related_name='transaction')
paid = models.BooleanField(default=False)
teller_no = models.CharField(max_length=20, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)

in views.py
---------------
if request.POST:
update_form = TransactionUpdateForm(request.POST, instance=txn)
txn.branch_name = Branch([int(k) for k,v in dic.iteritems() if v ==
update_form.cleaned_data['branch_name']][0]) # Forcefully store the key
if update_form.is_valid():
update_form.save()

The form worked perfectly in admin but not from my own custom ModelForm.

I don't know if my title is correct as I can see that Django saves the
selected element key in
the db and not its value which will be of type integer

I'm getting the error from my oen custom ModelForm:
<ul class="errorlist"><li>branch_name<ul class="errorlist"><li>Select a
valid choice.
That choice is not one of the available choices.</li></ul></li></ul>

Please how do I get the form saved with the selected branch value?

Please help.

Thanks
--
Odeyemi 'Kayode O.
http://www.sinati.com. t: @charyorde
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Jani Tiainen
2011-10-24 12:11:51 UTC
Permalink
Hi,

It really depends what is the result you're after.

Since your post doesn't indicate I made two guesses:

1) You want to store transaction in denormalized form. Meaning that
branch name will be stored as a string without any relation to existing
data.

This is good for history record keeping so if branches do change, value
stored in transaction is kept unchanged. I assume that this is also your
case.

Trick there is to define branch as a charfield, not foreign key.

class Transaction(models.Model):
branch_name = models.CharField(max_length=255)

And in a form you use form.ChoiceField (since it will be simple list of
choices, not list of model instances)

2) You want to use character field as a primary key.

This is very easy and doable, though it will kill relational queries
quite easily - strings do not make great indexes.

Just change your primary key to be something like:

class Branch(models.Model):
name = models.CharField(max_length=255, primary_key=True)

This approach has it's caveats specially when removing/renaming entries.
I wouldn't suggest this approach.
Post by Kayode Odeyemi
Hello all,
Is there a way to have a foreign key on a model which uses a type other
than Django's forceful
int type to make the storage for a ModelChoiceField widget?
forms.py
------------
return obj
branchqs = Branch.objects.values_list(u'name_branch', flat=True) #
use flat=True if values_list is called with a single field
branch_name = BranchModelChoiceField(branchqs)
model = Transaction
fields = ('teller_no', 'paid')
models.py
--------------
""" Branch """
bid = models.AutoField(primary_key=True)
name_branch = models.CharField(max_length=255)
db_table = u'branch'
return self.name_branch
id = models.AutoField(primary_key=True)
branch_name = models.ForeignKey(Branch, blank=True, null=True,
related_name='transaction')
paid = models.BooleanField(default=False)
teller_no = models.CharField(max_length=20, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
in views.py
---------------
update_form = TransactionUpdateForm(request.POST, instance=txn)
txn.branch_name = Branch([int(k) for k,v in dic.iteritems() if
v == update_form.cleaned_data['branch_name']][0]) # Forcefully store the key
update_form.save()
The form worked perfectly in admin but not from my own custom ModelForm.
I don't know if my title is correct as I can see that Django saves the
selected element key in
the db and not its value which will be of type integer
<ul class="errorlist"><li>branch_name<ul class="errorlist"><li>Select a
valid choice.
That choice is not one of the available choices.</li></ul></li></ul>
Please how do I get the form saved with the selected branch value?
Please help.
Thanks
--
Odeyemi 'Kayode O.
--
You received this message because you are subscribed to the Google
Groups "Django users" group.
To unsubscribe from this group, send email to
For more options, visit this group at
http://groups.google.com/group/django-users?hl=en.
--
Jani Tiainen
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Kayode Odeyemi
2011-10-24 12:48:40 UTC
Permalink
Post by Jani Tiainen
Hi,
It really depends what is the result you're after.
What I'm really after is such that whenever a transaction is edited and the
branch name on that
transaction is changed, the model Transaction should save the changed value
in the database.
Post by Jani Tiainen
1) You want to store transaction in denormalized form. Meaning that branch
name will be stored as a string without any relation to existing data.
It will have relation to Branch model name_branch field. So a transaction
just lookup the branch name from Branch model. This is the reason I made it
a ForeignKey.
Post by Jani Tiainen
This is good for history record keeping so if branches do change, value
stored in transaction is kept unchanged. I assume that this is also your
case.
Trick there is to define branch as a charfield, not foreign key.
branch_name = models.CharField(max_length=**255)
And in a form you use form.ChoiceField (since it will be simple list of
choices, not list of model instances)
If I use form.ChoiceField, how do I get the records from the database? The
choices are dynamic, which is why I have opted to go for ModelChoiceField.
Also, I don't really want to care if the ForeignKey field in Transaction
model is of type int or varchar, since I see that Mysql doesn't care about
this either. My major concern is why is Django reporting the error:

<ul class="errorlist"><li>branch_n
ame<ul class="errorlist"><li>Select a
valid choice.
That choice is not one of the available choices.</li></ul></li></ul>

Hence, saying the form is invalid? Is there any special thing I'm missing. I
have tried
to check the admin module source to see how it is done in there, but I've
not been able
to see much.

Please, what do I do in this case?
Post by Jani Tiainen
2) You want to use character field as a primary key.
This is very easy and doable, though it will kill relational queries quite
easily - strings do not make great indexes.
name = models.CharField(max_length=**255, primary_key=True)
This approach has it's caveats specially when removing/renaming entries. I
wouldn't suggest this approach.
This is not the issue in this use case.

Thanks for the help.
Post by Jani Tiainen
Post by Kayode Odeyemi
Hello all,
Is there a way to have a foreign key on a model which uses a type other
than Django's forceful
int type to make the storage for a ModelChoiceField widget?
forms.py
------------
return obj
branchqs = Branch.objects.values_list(u'**name_branch', flat=True) #
use flat=True if values_list is called with a single field
branch_name = BranchModelChoiceField(**branchqs)
model = Transaction
fields = ('teller_no', 'paid')
models.py
--------------
""" Branch """
bid = models.AutoField(primary_key=**True)
name_branch = models.CharField(max_length=**255)
db_table = u'branch'
return self.name_branch
id = models.AutoField(primary_key=**True)
branch_name = models.ForeignKey(Branch, blank=True, null=True,
related_name='transaction')
paid = models.BooleanField(default=**False)
teller_no = models.CharField(max_length=**20, blank=True)
created = models.DateTimeField(auto_now_**add=True)
updated = models.DateTimeField(auto_now=**True)
in views.py
---------------
update_form = TransactionUpdateForm(request.**POST, instance=txn)
txn.branch_name = Branch([int(k) for k,v in dic.iteritems() if
v == update_form.cleaned_data['**branch_name']][0]) # Forcefully store
the key
update_form.save()
The form worked perfectly in admin but not from my own custom ModelForm.
I don't know if my title is correct as I can see that Django saves the
selected element key in
the db and not its value which will be of type integer
<ul class="errorlist"><li>branch_**name<ul class="errorlist"><li>Select a
valid choice.
That choice is not one of the available choices.</li></ul></li></ul>
Please how do I get the form saved with the selected branch value?
Please help.
Thanks
--
Odeyemi 'Kayode O.
--
You received this message because you are subscribed to the Google
Groups "Django users" group.
To unsubscribe from this group, send email to
.
For more options, visit this group at
http://groups.google.com/**group/django-users?hl=en<http://groups.google.com/group/django-users?hl=en>
.
--
Jani Tiainen
--
You received this message because you are subscribed to the Google Groups
"Django users" group.
For more options, visit this group at http://groups.google.com/**
group/django-users?hl=en<http://groups.google.com/group/django-users?hl=en>
.
--
Odeyemi 'Kayode O.
http://www.sinati.com. t: @charyorde
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Tom Evans
2011-10-24 13:57:19 UTC
Permalink
Post by Kayode Odeyemi
Hello all,
        return obj
    branchqs = Branch.objects.values_list(u'name_branch', flat=True) # use
flat=True if values_list is called with a single field
    branch_name = BranchModelChoiceField(branchqs)
        model = Transaction
        fields = ('teller_no', 'paid')
Two things here. ModelChoiceField wants a real queryset, not a
ValuesQuerySet. The values of the model choice field should be the
primary keys of the model being chosen, which is not the case here
(which is why it fails).

The second thing is that queryset is evaluated precisely once, when
the form is instantiated for the first time. When you add a branch, it
will not appear in that form until the server is restarted.

Judging from your other reply, you don't actually want a foreign key
link to the branch, you simply want to store the name of the branch on
the transaction? If so, you should simply declare a ChoiceField and
supply it with choices derived from Branch.

Eg:

class TransactionUpdateForm(forms.ModelForm):
branch_name = ChoiceField(required=True)
def __init__(self, *args, **kwargs):
super(TransactionUpdateForm, self).__init__(*args, **kwargs)
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')

Cheers

Tom
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Kayode Odeyemi
2011-10-24 15:21:39 UTC
Permalink
Post by Tom Evans
Post by Kayode Odeyemi
Hello all,
return obj
branchqs = Branch.objects.values_list(u'name_branch', flat=True) #
use
Post by Kayode Odeyemi
flat=True if values_list is called with a single field
branch_name = BranchModelChoiceField(branchqs)
model = Transaction
fields = ('teller_no', 'paid')
Two things here. ModelChoiceField wants a real queryset, not a
ValuesQuerySet. The values of the model choice field should be the
primary keys of the model being chosen, which is not the case here
(which is why it fails).
The second thing is that queryset is evaluated precisely once, when
the form is instantiated for the first time. When you add a branch, it
will not appear in that form until the server is restarted.
Judging from your other reply, you don't actually want a foreign key
link to the branch, you simply want to store the name of the branch on
the transaction? If so, you should simply declare a ChoiceField and
supply it with choices derived from Branch.
branch_name = ChoiceField(required=True)
super(TransactionUpdateForm, self).__init__(*args, **kwargs)
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')
Thanks for writing in Tom.
I think what you did is the same as:

class TransactionUpdateForm(forms.ModelForm):
branchqs = Branch.objects.values_list('bid', u'name_branch')
branch_name = BranchModelChoiceField(branchqs)

I did try this earlier as well but didn't go to far with it.

But then in the template file, I have {{form.branch_name}} which results
into "Need more than 1 value to unpack error". How am I expected to print
the form
widget (select field) in a template?

--
Post by Tom Evans
You received this message because you are subscribed to the Google Groups
"Django users" group.
To unsubscribe from this group, send email to
For more options, visit this group at
http://groups.google.com/group/django-users?hl=en.
--
Odeyemi 'Kayode O.
http://www.sinati.com. t: @charyorde
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Tom Evans
2011-10-24 15:34:04 UTC
Permalink
Post by Tom Evans
Post by Tom Evans
Post by Kayode Odeyemi
Hello all,
        return obj
    branchqs = Branch.objects.values_list(u'name_branch', flat=True) # use
flat=True if values_list is called with a single field
    branch_name = BranchModelChoiceField(branchqs)
        model = Transaction
        fields = ('teller_no', 'paid')
Two things here. ModelChoiceField wants a real queryset, not a
ValuesQuerySet. The values of the model choice field should be the
primary keys of the model being chosen, which is not the case here
(which is why it fails).
The second thing is that queryset is evaluated precisely once, when
the form is instantiated for the first time. When you add a branch, it
will not appear in that form until the server is restarted.
Judging from your other reply, you don't actually want a foreign key
link to the branch, you simply want to store the name of the branch on
the transaction? If so, you should simply declare a ChoiceField and
supply it with choices derived from Branch.
 branch_name = ChoiceField(required=True)
   super(TransactionUpdateForm, self).__init__(*args, **kwargs)
   self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')
Thanks for writing in Tom.
    branchqs = Branch.objects.values_list('bid', u'name_branch')
    branch_name = BranchModelChoiceField(branchqs)
No! It really isn't.

1) In my one the queryset is created inside the form's __init__
method, so it gets executed correctly every time the form is
instantiated. In yours, the queryset is only evaluated once, so has
incorrect values as soon as a branch is added or deleted.
2) If you are using a ModelChoiceField, give it a QuerySet, not a
ValuesListQuerySet - really! This is why you get your unpacking
errors. However, don't you want to store the name of the branch on the
transaction, not the id of the branch? If so, you want a Choice field,
since you are not linking to another object, just denormalizing some
data from it into the Transaction model.
Post by Tom Evans
I did try this earlier as well but didn't go to far with it.
But then in the template file, I have {{form.branch_name}} which results
into "Need more than 1 value to unpack error". How am I expected to print
the form
widget (select field) in a template?
Assuming you don't give a ModelChoiceField an incorrect value for it's
queryset, it is just this:

{{ form.name_of_field }}


Cheers

Tom
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Kayode Odeyemi
2011-10-24 16:53:32 UTC
Permalink
Post by Tom Evans
Post by Tom Evans
Post by Tom Evans
Post by Kayode Odeyemi
Hello all,
return obj
branchqs = Branch.objects.values_list(u'name_branch', flat=True) # use
flat=True if values_list is called with a single field
branch_name = BranchModelChoiceField(branchqs)
model = Transaction
fields = ('teller_no', 'paid')
Two things here. ModelChoiceField wants a real queryset, not a
ValuesQuerySet. The values of the model choice field should be the
primary keys of the model being chosen, which is not the case here
(which is why it fails).
The second thing is that queryset is evaluated precisely once, when
the form is instantiated for the first time. When you add a branch, it
will not appear in that form until the server is restarted.
Judging from your other reply, you don't actually want a foreign key
link to the branch, you simply want to store the name of the branch on
the transaction? If so, you should simply declare a ChoiceField and
supply it with choices derived from Branch.
branch_name = ChoiceField(required=True)
super(TransactionUpdateForm, self).__init__(*args, **kwargs)
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')
Thanks for writing in Tom.
branchqs = Branch.objects.values_list('bid', u'name_branch')
branch_name = BranchModelChoiceField(branchqs)
No! It really isn't.
1) In my one the queryset is created inside the form's __init__
method, so it gets executed correctly every time the form is
instantiated. In yours, the queryset is only evaluated once, so has
incorrect values as soon as a branch is added or deleted.
2) If you are using a ModelChoiceField, give it a QuerySet, not a
ValuesListQuerySet - really! This is why you get your unpacking
errors. However, don't you want to store the name of the branch on the
transaction, not the id of the branch? If so, you want a Choice field,
since you are not linking to another object, just denormalizing some
data from it into the Transaction model.
Post by Tom Evans
I did try this earlier as well but didn't go to far with it.
But then in the template file, I have {{form.branch_name}} which results
into "Need more than 1 value to unpack error". How am I expected to print
the form
widget (select field) in a template?
Assuming you don't give a ModelChoiceField an incorrect value for it's
{{ form.name_of_field }}
I'm sorry but it doesn't just seem to work for me. I have tried
possibilities like:

self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch')

in template
----------------
{{form.branch_name}}

and

<select name="branch_name" class="form-select " id="id_branch_name" >
{% for choice in form.branch_name.field.choices %}
{% choice.name_branch %}
<option value="0">{% choice.name_branch %}</option>
{% endfor %}
</select>

Both reporting "Need more than 1 value to unpack error". Does this have
anything to do with if perhaps the branch_name db field contains NULL.
From the interactive shell, I get more that 1 result.

Thanks
--
Odeyemi 'Kayode O.
http://www.sinati.com. t: @charyorde
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Carl Meyer
2011-10-24 17:30:50 UTC
Permalink
Hi,
Post by Tom Evans
I'm sorry but it doesn't just seem to work for me. I have tried
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch')
I don't think you've read Tom's responses carefully. You're still
trying to use ".values()" here, resulting in a ValuesQuerySet which
returns dictionaries instead of a regular QuerySet which returns model
objects. That simply won't ever work. And that is the reason for the
"need more than one value to unpack" error; it has to do with the
format of the results returned, nothing to do with how many results
are returned by the query.

Carl
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Kayode Odeyemi
2011-10-25 08:50:17 UTC
Permalink
Post by Carl Meyer
Hi,
Post by Tom Evans
I'm sorry but it doesn't just seem to work for me. I have tried
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch', 'name_branch')
self.fields['branch_name'].choices =
Branch.objects.all().values('name_branch')
I don't think you've read Tom's responses carefully. You're still
trying to use ".values()" here, resulting in a ValuesQuerySet which
returns dictionaries instead of a regular QuerySet which returns model
objects. That simply won't ever work. And that is the reason for the
"need more than one value to unpack" error; it has to do with the
format of the results returned, nothing to do with how many results
are returned by the query.
Thanks Carl.
I was able to resolve it with this:

class TransactionUpdateForm(forms.ModelForm):
branch_name = ChoiceField(required=True)
def __init__(self, *args, **kwargs):
super(TransactionUpdateForm, self).__init__(*args, **kwargs)
br = [obj.name_branch for obj in Branch.objects.all()]
pos = [b for b in range(len(br))]
self.fields['branch_name'].choices = zip(pos, br)

Thanks Tom
--
Odeyemi 'Kayode O.
http://www.sinati.com. t: @charyorde
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-***@googlegroups.com.
To unsubscribe from this group, send email to django-users+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
Continue reading on narkive:
Loading...