How to tell django-tables2 which columns I would like to have in table? I know there is this Column attribute 'visible', which can be set to False.
However I have a model with many fields, and would like to display just some of them, so writing a complete list of all columns, just to tell that most of them will not be visible, does not seem the right approach.
What I am looking for is a way to provide list of column names to be displayed, if this is possible then maybe even give user the ability to select which columns he wants.
The other solution came to my mind - make that 'visible' attribute False by default, but since it is defined in Column class, I would still need to write a complete list.
Since I have not found any django-tables2 discussion forum, I ask here.
Example of specifying model fields
Your Model
class Product(model.Models):
name = model.CharField(max_length=20)
price = model.DecimalField(max_digit=9, decimal_places=2)
Your Table
class ProductTable(tables.Table):
actions = ProductActions(orderable=False) # custom tables.Column()
class Meta:
model = Product
fields = ('name', 'price', 'action') # fields to display
Also you can also use exclude
Related docs entry here
Related
There's a lot of questions worded similarly, but every single one I've seen is somebody trying to get some kind of data through a ManyToMany relationship before saving it. I'm not trying to use the relationship at all before saving, I just want to see if the user put anything there or not.
My model has a ForeignKey field pointing to a parent model, and two ManyToMany fields pointing to other models, but I only want users to be able to use one M2M field or the other, not both. This model is being edited through the admin as an inline on its parent.
models.py
class ProductSpecial(models.Model):
# name, slug, image, etc
class ProductSpecialAmount(models.Model):
special = models.ForeignKey(ProductSpecial, related_name='amounts', on_delete=models.CASCADE)
amount = models.IntegerField()
brands = models.ManyToManyField(ProductBrand, related_name='specials', blank=True)
lines = models.ManyToManyField(ProductLine, related_name='specials', blank=True)
admin.py
class ProductSpecialAmountInline(admin.StackedInline):
model = ProductSpecialAmount
# fieldsets, etc
#admin.register(ProductSpecial)
class ProductSpecialAdmin(admin.ModelAdmin):
inlines = [ProductSpecialAmountInline]
# fieldsets, etc
I only want users to be able to choose from brands or lines, but not both, and I would like to validate this before save and throw a validation error if necessary. My initial attempt was to just do...
class ProductSpecialAmount(models.Model):
# ...
def clean(self):
if self.brands and self.lines:
raise ValidationError('Please choose either brands or lines, not both', code='invalid')
...but that throws ValueError: "<ProductSpecialAmount: ProductSpecialAmount object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
I get that I can't actually query the related ProductBrand or ProductModel objects before this object is saved, but I don't actually want any data from those objects right now, I just want to know if the user left either of the fields blank or not, and am wondering if that's possible to see at the model level.
Whether you actually want to use the data from a field or just see if it is blank, the problem is caused by referencing the m2m field in any way before saving the object. I had a similar problem which I fixed using a custom form as per: https://stackoverflow.com/a/7986937/19837155
This might be more difficult when you're using inlines, but it may be the easiest way to solve your problem.
I'm writing a simple scaffolding app in django, however I'm still not able to access a models fields and attributes(e.g. CharField, max_length=100, null=True, etc...). I know about the _meta class of models, but as far as I know it only retrieves basic info about the model not the fields. Is there a away to achieve this?
Update:
you can find the answer in this article:
http://www.b-list.org/weblog/2007/nov/04/working-models/
You should use the get_field method to get info for a particular field:
field = ModelName._meta.get_field('field_name')
Then you may check various attributes of field, like field.blank, field.null, field.name etc.
If on the other hand you want to get a list of all fields of a Model you should use fields:
fields = ModelName._meta.fields
For example to get the name of all your model fields you can do something like:
field_names = ', '.join(f.name for f in fields)
Hm... also I just noticed that your question is a duplicate of Get model's fields in Django !
I posted this question on the django-users list, but haven't had a reply there yet.
I have models that look something like this:
class ProductGroup(models.Model):
name = models.CharField(max_length=10, primary_key=True)
def __unicode__(self): return self.name
class ProductRun(models.Model):
date = models.DateField(primary_key=True)
def __unicode__(self): return self.date.isoformat()
class CatalogItem(models.Model):
cid = models.CharField(max_length=25, primary_key=True)
group = models.ForeignKey(ProductGroup)
run = models.ForeignKey(ProductRun)
pnumber = models.IntegerField()
def __unicode__(self): return self.cid
class Meta:
unique_together = ('group', 'run', 'pnumber')
class Transaction(models.Model):
timestamp = models.DateTimeField()
user = models.ForeignKey(User)
item = models.ForeignKey(CatalogItem)
quantity = models.IntegerField()
price = models.FloatField()
Let's say there are about 10 ProductGroups and 10-20 relevant
ProductRuns at any given time. Each group has 20-200 distinct
product numbers (pnumber), so there are at least a few thousand
CatalogItems.
I am working on formsets for the Transaction model. Instead of a
single select menu with the several thousand CatalogItems for the
ForeignKey field, I want to substitute three drop-down menus, for
group, run, and pnumber, which uniquely identify the CatalogItem.
I'd also like to limit the choices in the second two drop-downs to
those runs and pnumbers which are available for the currently
selected product group (I can update them via AJAX if the user
changes the product group, but it's important that the initial page
load as described without relying on AJAX).
What's the best way to do this?
As a point of departure, here's what I've tried/considered so far:
My first approach was to exclude the item foreign key field from the
form, add the substitute dropdowns by overriding the add_fields
method of the formset, and then extract the data and populate the
fields manually on the model instances before saving them. It's
straightforward and pretty simple, but it's not very reusable and I
don't think it is the right way to do this.
My second approach was to create a new field which inherits both
MultiValueField and ModelChoiceField, and a corresponding
MultiWidget subclass. This seems like the right approach. As
Malcolm Tredinnick put it in
a django-users discussion,
"the 'smarts' of a field lie in the Field class."
The problem I'm having is when/where to fetch the lists of choices
from the db. The code I have now does it in the Field's __init__,
but that means I have to know which ProductGroup I'm dealing with
before I can even define the Form class, since I have to instantiate the
Field when I define the form. So I have a factory
function which I call at the last minute from my view--after I know
what CatalogItems I have and which product group they're in--to
create form/formset classes and instantiate them. It works, but I
wonder if there's a better way. After all, the field should be
able to determine the correct choices much later on, once it knows
its current value.
Another problem is that my implementation limits the entire formset
to transactions relating to (CatalogItems from) a single
ProductGroup.
A third possibility I'm entertaining is to put it all in the Widget
class. Once I have the related model instance, or the cid, or
whatever the widget is given, I can get the ProductGroup and
construct the drop-downs. This would solve the issues with my
second approach, but doesn't seem like the right approach.
One way of setting field choices of a form in a formset is in the form's __init__ method by overwriting the self.fields['field_name'].choices, but since a more dynamic approach is desired, here is what works in a view:
from django.forms.models import modelformset_factory
user_choices = [(1, 'something'), (2, 'something_else')] # some basic choices
PurchaserChoiceFormSet = modelformset_factory(PurchaserChoice, form=PurchaserChoiceForm, extra=5, max_num=5)
my_formset = PurchaserChoiceFormSet(self.request.POST or None, queryset=worksheet_choices)
# and now for the magical for loop
for choice_form in my_formset:
choice_form.fields['model'].choices = user_choices
I wasn't able to find the answer for this but tried it out and it works in Django 1.6.5. I figured it out since formsets and for loops seem to go so well together :)
I ended up sticking with the second approach, but I'm convinced now that it was the Short Way That Was Very Long. I had to dig around a bit in the ModelForm and FormField innards, and IMO the complexity outweighs the minimal benefits.
What I wrote in the question about the first approach, "It's straightforward and pretty simple," should have been the tip-off.
I have some newbie questions about Django.
I want to write a generic ticket-management system, where the administrator of the site should be able to add custom fields to a ticket. It seems that the database tables are generated on initialization, so it is not clear to me how to add custom fields at runtime.
One way is to have a long list of fields of different types, all nullable, and let the administrator rename/select the fields she needs. Is there a better design?
Thanks!
I'm currently in charge of maintaining a similar site where a treatment for a medical condition is listed and there can be arbitrary number of "cases" which are user-posted experiences for that treatment/condition combo attached.
The method my company used to set it up was to have an Entry object which would be analogous to the custom field you described, which has a Foreign Key referencing the treatment/condition to which it belongs.
Then when we want to get all the entries for a particular treatment/condition combo, we simply do an
Entry.objects.filter(condition=ID)
So, in your case, I would suggest having a Ticket model, and an "Entry" style model which contains a Foreign Key reference to the Ticket to which it belongs.
I would make something like the code below. Store extra attributes in an attribute model. Store the values in AttributeValue.
class Ticket(models.Model):
name = models.CharField(max_length=200)
class Attribute(models.Model):
name = models.CharField(max_length=200)
class AttributeValues(models.Model):
attribute = models.ForeignKey(Attribute)
ticket = models.ForeignKey(Ticket)
value = models.CharField(max_length=200)
I've essentially got two tables: Page(PK=url) and PageProperty(PK=url+name).
Here is how I have my Models set up:
class Page(model.Model):
url = model.CharField(primary_key=True, max_length=255, db_column='url')
#.....
class PageProperty(model.Model):
# table with compound key (url + name)
url = model.ForeignKey('Page', to_field='url', db_column='url', primary_key=True)
name = model.CharField(primary_key=True, max_length=20)
value = model.TextField()
I have a ModelAdmin set up so I can Inline edit PageProperty(s) from Page. Its a legacy database and I know there's a lot of data in there. But the Admin is only showing ONE of the PagePropertys, not all.
I think you might need to apply the extra option to your TabularInline. Example:
class PagePropertyInline(admin.TabularInline):
model = PageProperty
extra = 3
You could probably do some magic to make the amount of extra items dynamic (such as the number of PageProperty objects for a given Page, but I'll leave that up to you.
I would suggest further reading on InlineModelAdmin options and Formsets.
Because it felt as thought a non-integer primary key was too much against the grain, I ended up buckling down and migrating the schema to use an auto generated integer pk for both tables. After that everything was smooth sailing again and the Inlines worked perfectly.