django-tables2: Adding columns/fields dynamically - python

I want to add a new field when somebody checks an option in my web page ("Add column 'external id' ")
table = IssueTable(issue_list)
show_ext = request.GET.get('show_ext', 0)
if show_ext:
table._meta.fields = table._meta.fields + ('external_id',)
I thought doing that it was the solution but I didn't succeed. The external_id is part of the model. table._meta.fields doesnt get updated and of course I cannot see the column in the table when this one is rendered.
class IssueTable(tables.Table):
def render_id(self, value, record):
if record.status.upper() in ['RESOLVED', 'INVALID']:
return mark_safe('<s>#%s</s>' % (reverse('issues.views.view', args=(record.pk,)), record.pk))
else:
return mark_safe('#%s' % (reverse('issues.views.view', args=(record.pk,)), record.pk))
def render_title(self, value, record):
return mark_safe('%s' % (reverse('issues.views.view', args=(record.id,)), value))
class Meta(object):
model = Issue
fields = ('id', 'title', 'product', 'type', 'priority', 'status', 'created')
attrs = {'class': 'table table-bordered'}
empty_text = _('No records found')
Any ideas?

Related

Odoo: Values of Many2many with dynamic domain aren't getting saved

I am trying to dynamically change the values of a many2many field products_ids based on multiple onchange functions of other fields (e.g. brand_id and origin_id).
So far everything is working great and it does show the expected values, but once i hit the save button the values of the many2many field disappear
class CustomModifyPrice(models.Model):
brand_id = fields.Many2many(comodel_name="custom.brand", string="Brand", required=False, )
origin_id = fields.Many2many(comodel_name="custom.country", string="Origin", required=False, )
product_ids = fields.Many2many(comodel_name="custom.product", string="Products", readonly=True, )
search_terms = {}
product_ids_list = []
#api.onchange('brand_id')
def onchange_change_brand(self):
for rec in self:
product_brands = []
for prod_brand in rec.brand_id:
product_brands.append(prod_brand.id)
rec.search_terms["product_brands"] = product_brands
rec.get_products()
#api.onchange('origin_id')
def onchange_change_origin(self):
for rec in self:
product_origins = []
for prod_origin in rec.origin_id:
product_origins.append(prod_origin.id)
rec.search_terms["product_origins"] = product_origins
rec.get_products()
def get_products(self):
domain = []
self.product_ids_list = []
if 'product_brands' in self.search_terms:
product_brands = self.search_terms['product_brands']
if product_brands:
tuple1 = ('brand_id', 'in', product_brands)
domain.append(tuple1)
if 'product_origins' in self.search_terms:
product_origins = self.search_terms['product_origins']
if product_origins:
tuple1 = ('country_id', 'in', product_origins)
domain.append(tuple1)
if domain:
products = self.env['custom.product'].search(domain)
if products.ids:
for prod in products:
self.product_ids_list.append(prod.id)
self.product_ids = [(6, False, self.product_ids_list)]
Make sure force_save="1" is placed as an attribute in your field (xml file)

api.constrain for give condition if one of field is empty

I tried to use api.constrain in odoo. The case is, I want to give condition to odoo if user missing one field, odoo will give some warning like alert, and user have to give some input to the previous field. This is my code :
class Provinc(models.Model):
_name = 'provinsi.model'
_rec_name ='prov'
prov = fields.Char(
string='Provinsi',
)
res_partner_ids = fields.One2many(
'res.partner',
'provinsi'
city_id = fields.One2many(
'city.model',
'provinsi_id'
)
class city(models.Model):
_name = 'kota.model'
_rec_name ='city'
city = fields.Char(
string='City',
)
res_partner_city = fields.One2many(
'res.partner',
'city'
)
provinsi_id = fields.Many2one(
'provinsi.model',
string='provinsi',
)
class Master_data(models.Model):
_inherit = 'res.partner'
provinsi = fields.Many2one(
'provinsi.model',
'provinsi',
)
city = fields.Many2one(
'city.model',
'city',
)
#api.onchange('provinsi')
def show_provinsi(self):
return {'domain': {'city': [('provinsi_id', '=', self.provinsi.id)]}}
#api.constrains('provinsi')
#api.onchange('city')
def show_kota(self):
for record in self:
if record.provinsi == False:
raise ValidationError("is gonna be error")
I have tried 2 ways. First, I put the api.constrain insode class City, and it's doesn't work,second ways I tried put the api.constrain like the code inside class master, and the result remains the same.
you only need onchang not any constrains to display warning!
#api.onchange('city')
def show_kota(self):
if self.city and not self.provinsi:
raise ValidationError("is gonna be error")
now if you select value in city field and provinsi missing value then it will display error msg
guessing field "city" and "provinsi" is in 'res.partner' form view!
If you want to check a field value is empty or not, just use required ="1" like:
<field name="provinsi" required="1"/>

Django not see the table in generic relation

I'm working with Django 1.7.2 with generic relation (reason: project has two databases), and after import I don't want to have overwritten database.
I want to show only active season
Here is my model, is overwritten after import:
class Season(models.Model):
name = models.CharField('Name', max_length=255)
start = models.DateField('Start')
end = models.DateField('End')
def __unicode__(self):
return self.name
In another place in models.py file I create Generic relation to another model (SaleAndCycle) in another database:
def extend_models(sender, **kwargs):
if sender._meta.app_label != 'extend':
return
def set_stati(self, database='extend', save=True):
online_before = self.online
self.online = True
for flag in self.flags:
if flag.flag == 'offline':
self.online = False
if save and self.online != online_before:
self.save(using=database)
for name, name_singular, name_plural in (
('Season', 'Season', 'Seasons'):
if sender.__name__ == name:
sender._meta.verbose_name = name_singular
sender._meta.verbose_name_plural = name_plural
if sender.__name__ == 'Season':
sender.add_to_class('sale_and_cycles', generic.GenericRelation(SaleAndCycle))
sender.add_to_class('is_sale_active', property(
lambda o: o.sale_and_cycles.using('default')))
sender.add_to_class('is_cyclic_event_active', property(
lambda o: o.sale_and_cycles.using('default')))
sender.add_to_class('cycle_link', property(
lambda o: o.sale_and_cycles.using('default')))
I want to show all active Seasons for not login user:
def get_queryset(self):
seasons = Season.objects.all()
if not self.request.user.is_superuser:
all_seasons = Season.objects.filter(events_isactiveflags__is_active=True)
print all_seasons
I get error:
no such table: events_isactiveflag
But this table exists in my database.
by the line of code
Season.objects.filter(events_isactiveflags__is_active=True)
here events_isactiveflags__is_active=True means events_isactiveflags is a table and is_active is a column of the table. So no such table found named events_isactiveflags . if you are trying to get active sessions you can try this code
Session.objects.filter(expire_date__gte=timezone.now())
delete db and once again run syncdb

Add a text field to a form- populate it with a 'default value' using Python/Django

I am working on a Python/Django project, and one of the fields on a form is currently broken (it's a datetimepicker field- the issue is that you can't select dates beyond 01/01/2017). It seems this is because the library being used to add the datetimepicker is no longer supported).
I will look into implementing a datetimepicker myself, but need to allow the user to select dates beyond the 01/01/2017 on this form- in order to save the dates and times for scheduled meetings, etc.
My thoughts for the moment are to create a temporary fix, by adding simple EasyText fields to the form for the date/ time. I am aware that this means that the data entered will not be validated, so there is room for user input error, but it will enable the user to set dates/ times for meeting at least temporarily, until I fix the issue with the current datetimepicker field.
The form that I want to add these fields to is defined in forms.py with:
class PostDepMeetingForm(ValidatedForm):
postdep_meeting_date = MoonDateTimeField(required=False, widget=forms.DateTimeInput(format='%d/%m/%Y %H:%M', attrs=({'class':'datetimepicker'})))
planning_who = forms.CharField(required=False)
general_notes = EasyText(label='Survey notes')
#ERF(16/12/2016 # 1345) Add a text area for the meeting date & time
meeting_date_time = EasyText(label='Meeting date & time')
class Meta:
model = Survey
fields = ('postdep_meeting_date', 'planning_who','building_regs','general_notes', 'meeting_date_time')
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', {})
project = instance.project
try:
postdep_meeting = project.meetings.get(purpose=3)
self.postdep_meeting_id = postdep_meeting.id
self.postdep_meeting_creator = postdep_meeting.event_creator or ''
postdep_meeting_date = postdep_meeting.date
general_notes = postdep_meeting.notes
#ERF(16/12/2016 # 1420) Try setting the meeting_date_time EasyText's default value to postdep_meeting.date & postdep_meeting.time
#meeting_date_time
except ObjectDoesNotExist:
postdep_meeting_date = None
general_notes = None
try: planning_who = project.assigned.select_related('employee').get(role=Role.PL).employee.id
except ObjectDoesNotExist: planning_who = None
initial = kwargs.get('initial', {})
initial={
'postdep_meeting_date': postdep_meeting_date,
'general_notes': general_notes,
'planning_who': planning_who,
}
kwargs['initial'] = initial
super(PostDepMeetingForm, self).__init__(*args, **kwargs)
self.fields['planning_who'] = AutoFlexiSelect(model='e', choices=get_choices('ARCHITECT_CHOICES') + [('*', 'Other')], label="Who", current_id=planning_who,)
self.fields['postdep_meeting_date'].widget.attrs.update({'data-meeting-id': getattr(self,'postdep_meeting_id', ''), 'data-meeting-creator': getattr(self,'postdep_meeting_id', '')})
def save(self, commit=True):
project = self.instance.project
data = self.cleaned_data
if 'postdep_meeting_date' in self.changed_data:
postdep_meeting = Meeting.objects.get_or_create(project=project, purpose=3)[0]
postdep_meeting.date = data['postdep_meeting_date']
postdep_meeting.notes = data['general_notes']
postdep_meeting.save()
if data['planning_who']:
try: project.assigned.select_related('employee').get(role=Role.PL).delete()
except ObjectDoesNotExist: pass
pe = ProjectEmployee.objects.create(role=Role.PL, project=project, employee_id=data['planning_who'])
return super(PostDepMeetingForm, self).save(commit=commit)
I have added the EasyText field, called meeting_date_time to the form, and can see it displayed on the form when I view the page in the browser.
What I'm unsure about how to do now- is save any input entered by the user, and have whatever they have input into that field displayed there automatically the next time they browse to that page... Will I need to add a new field to the project model to save this information?
Edit
As requested in a comment, ValidatedForm is defined with:
class ValidatedForm(forms.ModelForm):
error_css_class = 'error-field'
required_css_class = 'required'
def __init__(self, *args, **kwargs):
super(ValidatedForm, self).__init__(*args, **kwargs)
# for k, field in self.fields.items():
# if 'required' in field.error_messages:
# # print 'field error', field, field
# # field.widget.attrs['class'] = 'error-field'
# print 'field error', k
# field.error_messages['required'] = '*'
# else:
# print 'Field erroR', field
and EasyText is a 'text area' for storing user input on the form, defined with:
def EasyText(field_class="medium", readonly=False, placeholder='', extras={}, **kwargs):
textarea = forms.CharField(widget=TextareaWidget(attrs={'class':field_class, 'readonly':readonly, 'placeholder': placeholder}))
if extras:
textarea.widget.attrs.update(**extras)
try: textarea.label = kwargs['label']
except KeyError: pass
try: textarea.required = kwargs['required']
except KeyError: textarea.required = False
return textarea

Django custom Widget failing on choices above 1 (enter whole number)

I wanted a multiState clickbox. So I spend some free time on a nice Django solution that makes it:
class MultiStateChoiceInput(forms.widgets.ChoiceInput):
input_type = 'radio'
def __init__(self, name, value, attrs, choice, index, label_id):
# Override to use the label_id which is upped with 1
if 'id' in attrs:
self.label_id = attrs['id']+ "_%d" % label_id
super(MultiStateChoiceInput, self).__init__(name, value, attrs, choice, index)
self.value = force_text(self.value)
#property
def id_for_label(self):
return self.label_id
def render(self, name=None, value=None, attrs=None, choices=()):
if self.id_for_label:
label_for = format_html(' for="{}"', self.id_for_label)
else:
label_for = ''
attrs = dict(self.attrs, **attrs) if attrs else self.attrs
return format_html(
'{} <label{}>{}</label>', self.tag(attrs), label_for, self.choice_label
)
class MultiStateRenderer(forms.widgets.ChoiceFieldRenderer):
choice_input_class = MultiStateChoiceInput
outer_html = '<span class="cyclestate">{content}</span>'
inner_html = '{choice_value}{sub_widgets}'
def render(self):
"""
Outputs a <ul> for this set of choice fields.
If an id was given to the field, it is applied to the <ul> (each
item in the list will get an id of `$id_$i`).
# upgraded with the label_id
"""
id_ = self.attrs.get('id')
output = []
for i, choice in enumerate(self.choices):
choice_value, choice_label = choice
if isinstance(choice_label, (tuple, list)):
attrs_plus = self.attrs.copy()
if id_:
attrs_plus['id'] += '_{}'.format(i)
sub_ul_renderer = self.__class__(
name=self.name,
value=self.value,
attrs=attrs_plus,
choices=choice_label,
label_id = (i+1) % (len(self.choices)) # label_id is next one
)
sub_ul_renderer.choice_input_class = self.choice_input_class
output.append(format_html(self.inner_html, choice_value=choice_value,
sub_widgets=sub_ul_renderer.render()))
else:
w = self.choice_input_class(self.name, self.value,
self.attrs.copy(), choice, i, label_id = (i+1) % (len(self.choices))) # label_id is next one
output.append(format_html(self.inner_html,
choice_value=force_text(w), sub_widgets=''))
return format_html(self.outer_html,
id_attr=format_html(' id="{}"', id_) if id_ else '',
content=mark_safe('\n'.join(output)))
class MultiStateSelectWidget(forms.widgets.RendererMixin, forms.widgets.Select):
''' This widget enables multistate clickable toggles
Requires some css as well (see .cyclestate)
'''
renderer = MultiStateRenderer
This creates a form like is explained here https://stackoverflow.com/a/33455783/3849359 and where a click toggles the next state until it reached the and and then continues at the beginning.
The form is called in my view like:
SomeFormSet= modelformset_factory(myModel, form=myModelForm, extra=0)
SomeFormSet.form = staticmethod(curry(myModelForm, somevariable=somevariable))
formset = SomeFormSet(request.POST or None, queryset=somequeryset)
And forms.py is:
class myModelForm(forms.ModelForm):
CHOICES = (
(0, _('a')),
(1, _('b')),
(2, _('c')),
(3, _('d')),
)
field = forms.IntegerField(widget=MultiStateSelectWidget(choices=CHOICES))
class Meta:
model = MyModal
fields = ('field',)
widgets = {'id': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
self.variable= kwargs.pop('variable')
super(myModelForm, self).__init__(*args, **kwargs)
for field in myModelForm.fields:
if self.instance.pk:
if not getattr(self.instance, field):
self.initial[field]= 0
else:
self.initial[field]= 1
if anothercondition:
self.initial[field] = 3
else:
self.initial[field] = 2
I thought it worked very well. And the clicking and saving does work wel (I have a custom save method). Except when the form field has a value of 2 or 3, then it suddenly failes with the error message: 'field' should be a whole number.
If anyone could help that would be great, as I'm out of ideas!
EDIT: Just in case... I have checked the POST and it is great. The only problem is that Django somewhere in parsing the POST loses the value completely (it becomes None) if the value is a 2 and I have no idea why.
EDIT2: It seems that the Django ModelForm does also do model validation. And the model is a BooleanField, which is the reason why it fails. If anyone knows a good way to override it, that would be nice!
#edgarzamora Your comment is not the answer, but it is close!
I removed the 'field' from the Form class Meta, so it looked like:
class Meta:
model = MyModal
fields = ('',)
widgets = {'id': forms.HiddenInput(),
}
And now everything works, because I have my custom save method... So stupid, it costed me hours! Thanks!

Categories

Resources