This is a very very basic question and i have already searched and tried lots of ways to do it but i want to know the good practice/best method to go about it.
There is a table in which i am trying to store the user selected code from another table. What is want is
A model form combo box which shows description field value while saves its respective pos_code in the table.
This is my model and forms:
pos_code = forms.ModelChoiceField(queryset=Positions.objects)
Here i want to insert the pos_code against the user selected description:
class TmpPlInvoice(models.Model):
voucher_id = models.CharField(primary_key=True, max_length=10)
pos_code = models.ForeignKey(Positions, models.DO_NOTHING, db_column='pos_code', blank=True, null=True)
class Meta:
managed = False
db_table = 'tmp_pl_invoice'
I'm getting the choice field from this model:
class Positions(models.Model):
pos_code = models.CharField(primary_key=True, max_length=10, blank=True, null=True)
description = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'positions'
def __unicode__(self):
return self.description
But it gives me description instead of pos_code. I know that I am returning description but I need to show it to user and get code in the views.
Here is my full form
class TmpForm(forms.ModelForm):
description = forms.ModelChoiceField(queryset=Positions.objects.all())
class Meta:
model = TmpPlInvoice
exclude = ['net_amt', 'post_date', 'address', 'posted', 'voucher_date', 'particulars']
What i have
[IMG]http://i68.tinypic.com/zj89yx.jpg[/IMG]
Current form output
{'voucher_id': u'3452345', 'description': Positions: Premier Industrial Chemicals}
I can't use this 'description'. I need to save the code of Premier Industrial Chemicals in my TmpForm
What i need
[IMG]http://i66.tinypic.com/nh0x2a.jpg[/IMG]
Desired form output
{'voucher_id': u'3452345', 'pos_code': 0001}
This model form saved my life. The MyModelChoiceField class shows the label but send id on the backend.
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.description
class TmpFormm(forms.ModelForm):
pos_code = MyModelChoiceField(queryset=Positions.objects.all(), widget=forms.Select(attrs={'class': 'select2_single form-control', 'blank': 'True'}))
Related
i am trying to replicate the category structure sites like Ebay use when posting a product. For example, if you want to post an Iphone for sale, you go to post an ad, choose a category from the drop down menu on the form('electronics & computers'), then choose a subcategory of "phones", then the last sub category of "iphone". To create this structure i am using django-categories. Creating a product in the admin page works file, the admin form allows me to choose from a drop down menu of every category, however i can't seem to replicate that same process on my own form, to give users the ability to choose from the many categories.
If your not aware if django-categories it is a Modified Preorder Tree Traversal.
Here is my advert model
class Advert(models.Model):
title = models.CharField(max_length=100, blank=False, null=False)
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
image = models.ImageField(upload_to='media/', blank=True, null=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
quantity = models.IntegerField(blank=False, null=False)
condition = models.CharField(max_length=10, choices=COND_CATEGORIES, blank=False, null=False)
price = models.DecimalField(decimal_places=2, max_digits=14, blank=False, null=False)
description = models.TextField(blank=False, null=False)
date_posted = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
featured = models.BooleanField(default=False)
search_vector = SearchVectorField(null=True, blank=True)
Here is the category model
class Category(CategoryBase):
class Meta:
verbose_name_plural = 'categories'
And here i the form that allows users to post an advert
class PostAdvertForm(forms.ModelForm):
title = forms.CharField(label='Ad Title', required=True)
category = forms.ChoiceField(choices=Advert.category, label='Choose a category', required=True)
price = forms.DecimalField(label='Price', required=True, widget=forms.TextInput())
description = forms.CharField(widget=forms.Textarea(attrs={'placeholder':
('Please provide a detailed description'),
'autofocus': 'autofocus'}), label='Description', required=True)
condition = forms.ChoiceField(choices=Advert.COND_CATEGORIES, label='Condition', required=True)
quantity = forms.IntegerField(label='Quantity', required=True, widget=forms.TextInput())
image = forms.ImageField(label='Upload an image', required=False)
class Meta:
model = Advert
fields = (
'title', 'category', 'quantity', 'condition', 'price', 'description',
'image')
Using advert.category on the choice field is not working due to "ForwardManyToOneDescriptor' object is not iterable".
My question is how can i get the category list to appear on the choicefield?
EDIT:
Was just wondering, would this be possible to implement entirely with jquery on the front end and just send through the choosen category through the cleaned data? This way i would not even need to bother using django-categories on the backend, i could just store a massive load of categories.
2nd EDIT:
Looks like i got the first part of this to work, i have pasted the new form code below:
at = Advert.category.get_queryset()
class PostAdvertForm(forms.ModelForm):
title = forms.CharField(label='Ad Title', required=True)
category = forms.ModelChoiceField(queryset=cat, label='Choose a category', required=True)
price = forms.DecimalField(label='Price', required=True, widget=forms.TextInput())
description = forms.CharField(widget=forms.Textarea(attrs={'placeholder':
('Please provide a detailed description'),
'autofocus': 'autofocus'}), label='Description', required=True)
condition = forms.ChoiceField(choices=Advert.COND_CATEGORIES, label='Condition', required=True)
quantity = forms.IntegerField(label='Quantity', required=True, widget=forms.TextInput())
image = forms.ImageField(label='Upload an image', required=False)
class Meta:
model = Advert
fields = (
'title', 'category', 'quantity', 'condition', 'price', 'description',
'image')
I have never used jquery before, is that the best option to create a flowing drop down style menu? I must make it so they can't choose a parent category, only the bottom child category.
Define a function update_form_field_choices (for example in utils.py):
def update_form_field_choices(field, choices):
"""
Update both field.choices and field.widget.choices to the same value (list of choices).
"""
field.choices = choices
field.widget.choices = choices
Then, define method __init__ in PostAdvertForm:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
And in this function, call this function, and replace your_choices() with a function that returns the correct choices. Remember the choices format (a tuple of tuples).
update_form_field_choices(field=self.fields['category'], choices=your_choices())
Also remember, ModelForm defines the form fields for you, so you don't have to define them explicitly, unless you want to change something in the default definition.
I've got the following Situation, I have a rather large legacy model (which works nonetheless well) and need one of its fields as a distinct dropdown for one of my forms:
Legacy Table:
class SummaryView(models.Model):
...
Period = models.CharField(db_column='Period', max_length=10, blank=True, null=True)
...
def __str__(self):
return self.Period
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'MC_AUT_SummaryView'
Internal Model:
class BillCycle(models.Model):
...
Name = models.CharField(max_length=100, verbose_name='Name')
Period = models.CharField(max_length=10, null=True, blank=True)
Version = models.FloatField(verbose_name='Version', default=1.0)
Type = models.CharField(max_length=100, verbose_name='Type', choices=billcycle_type_choices)
Association = models.ForeignKey(BillCycleAssociation, on_delete=models.DO_NOTHING)
...
def __str__(self):
return self.Name
Since I don't want to connect them via a Foreign Key (as the SummaryView is not managed by Django) I tried a solution which I already used quite a few times. In my forms I create a ModelChoiceField which points to my Legacy Model:
class BillcycleModelForm(forms.ModelForm):
period_tmp = forms.ModelChoiceField(queryset=SummaryView.objects.values_list('Period', flat=True).distinct(),
required=False, label='Period')
....
class Meta:
model = BillCycle
fields = ['Name', 'Type', 'Association', 'period_tmp']
And in my view I try to over-write the Period Field from my internal Model with users form input:
def billcycle_create(request, template_name='XXX'):
form = BillcycleModelForm(request.POST or None)
data = request.POST.copy()
username = request.user
print("Data:")
print(data)
if form.is_valid():
initial_obj = form.save(commit=False)
initial_obj.ModifiedBy = username
initial_obj.Period = form.cleaned_data['period_tmp']
initial_obj.Status = 'Creating...'
print("initial object:")
print(initial_obj)
form.save()
....
So far so good:
Drop Down is rendered correctly
In my print Statement in the View ("data") I see that the desired infos are there:
'Type': ['Create/Delta'], 'Association': ['CP'], 'period_tmp': ['2019-12']
Still I get a Select a valid choice. That choice is not one of the available choices. Error in the forms. Any ideas??
Hi I have the following django model:
class Issue(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField(auto_now=True)
assignee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignee')
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner', null=True, blank=True)
description = models.TextField()
state = models.IntegerField(choices=STATUS_CHOICES, default=1)
priority = models.IntegerField(choices=RELEVANCE_CHOICES, default=2)
expired_date = models.DateField(auto_now=False, null=True, blank=True)
and a form which allow a user to create an Issue instance:
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title', 'description', 'assignee', 'state', 'priority', 'expired_date')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "Titolo"
self.fields['description'].label = "Descrizione"
self.fields['state'].label = "Stato"
self.fields['priority'].label = "Priorità"
self.fields['expired_date'].label = "Termine"
self.fields['expired_date'].widget.attrs.update({'class': 'datepicker'})
self.fields['assignee'] = forms.MultipleChoiceField(
choices=self.fields['assignee'].choices,
widget=forms.CheckboxSelectMultiple,
label=("Assegnatario")
)
def clean(self):
cleaned_data = super().clean()
user_id = [i for i in cleaned_data['assignee']]
cleaned_data['assignee'] = [User.objects.get(id=i) for i in user_id]
return cleaned_data
I render this form and the field assignee is a checkbox.
I would like to be able to choose several assignee for the same issue, but I got an error because the Issue model expect just one User instance
How can I modify my model Issue in order to get more than one user ?
Thanks
you can create a new class and name it Issue_Instance where every Issue Object can have an assignee as a foreign key the problem that the relation is one to many because you have to choose more than one assignee and Django doesn't support the idea of having Array or List of Foreign Keys(I don't know any frame works that do :=) ) so I would suggest creating a new class or make the foreign key relation one-to-many key field read about it it will be very useful to solve your problem
I've been trying to solve this problem for a couple of days now, getting quite desperate. See the commented out code snippets for some of the things I've tried but didn't work.
Problem: How can I limit the values in the category field of the IngredientForm to only those belonging to the currently logged in user?
views.py
#login_required
def apphome(request):
IngrFormSet = modelformset_factory(Ingredient, extra=1, fields=('name', 'category'))
# Attempt #1 (not working; error: 'IngredientFormFormSet' object has no attribute 'fields')
# ingrformset = IngrFormSet(prefix='ingr', queryset=Ingredient.objects.none())
# ingrformset.fields['category'].queryset = Category.objects.filter(user=request.user)
# Attempt #2 (doesn't work)
# ingrformset = IngrFormSet(prefix='ingr', queryset=Ingredient.objects.filter(category__user_id = request.user.id))
models.py:
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, null=True, blank=True)
class Ingredient(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(Category, null=True, blank=True)
counter = models.IntegerField(default=0)
forms.py:
class IngredientForm(ModelForm):
class Meta:
model = Ingredient
fields = ('name', 'category')
UPDATE: I've made some progress but the solution is currently hard-coded and not really usable:
I found out I can control the categoryform field via form class and then pass the form in the view like this:
#forms.py
class IngredientForm(ModelForm):
category = forms.ModelChoiceField(queryset = Category.objects.filter(user_id = 1))
class Meta:
model = Ingredient
fields = ('name', 'category')
#views.py
IngrFormSet = modelformset_factory(Ingredient, form = IngredientForm, extra=1, fields=('name', 'category'))
The above produces the result I need but obviously the user is hardcoded. I need it to be dynamic (i.e. current user). I tried some solutions for accessing the request.user in forms.py but those didn't work.
Any ideas how to move forward?
You don't need any kind of custom forms. You can change the queryset of category field as:
IngrFormSet = modelformset_factory(Ingredient, extra=1, fields=('name', 'category'))
IngrFormSet.form.base_fields['category'].queryset = Category.objects.filter(user__id=request.user.id)
Category.objects.filter(user=request.user)
returns a list object for the initial value in your form which makes little sense.
Try instead
Category.objects.get(user=request.user)
or
Category.objects.filter(user=request.user)[0]
I the following in the models.py:
class Item(models.Model):
date = models.DateField(_('date'), blank=True, null=True)
description = models.CharField(_('description'), max_length=255)
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
object_id = models.PositiveIntegerField(_('object id'), db_index=True)
object = generic.GenericForeignKey('content_type', 'object_id')
class ItemAccountAmountRef(Item):
""" Items of which a Quote or an Invoice exists. """
amount = models.DecimalField(max_digits=10, decimal_places=2)
reference = models.CharField(max_length=200)
debit_account = models.ForeignKey(Account, related_name='receivables_receipt_debit_account')
credit_account = models.ForeignKey(Account, related_name='receivables_receipt_credit_account')
class PaymentItem(ItemAccountAmountRef):
pass
class Payment(models.Model):
invoice = models.ManyToManyField(Invoice, null=True, blank=True)
date = models.DateField('date')
attachments = generic.GenericRelation(Attachment)
site = models.ForeignKey(Site, related_name='payment_site', null=True, blank=True
items = generic.GenericRelation(PaymentItem)
in the admin.py:
class PaymentItemInline(generic.GenericTabularInline):
model = PaymentItem
form = PaymentItemForm
class PaymentAdmin(admin.ModelAdmin):
inlines = [PaymentItemInline]
in forms.py:
class PaymentItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PaymentItemForm, self).__init__(*args, **kwargs)
self.fields['credit_account'].label = "Bank Account"
In the PaymentItemInline the label is not changing. I have tried changing other attributes e.g. class which work. If I run through the init in debug mode I can see that the label variable is changing however when the form is rendered the field is still labelled credit account. Any suggestions?
You're 98% of the way there. Instead of trying to futz with the form field in __init__, just redefine it in your ModelForm. If you name it the same thing, django will be able to figure out that it is supposed to validate & save to the ForeignKey field. You can use the same formula to change a Field or Widget completely for a given field in a ModelForm.
You can find the default form field types for each model field type here: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#field-types
class PaymentItemForm(forms.ModelForm):
credit_account = forms.ModelChoiceField(label="Bank Account", queryset=Account.objects.all())
That's it. No need to override any functions at all : )
Incidentally, the docs for this field are here: https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield