I'm facing a very weird behaviour of Django and Django-tables2.
I use table to help to render multiple types of documents.
For simplicity, there are two types of documents - 'faktura' and 'dobropis'.
'dobropis' has to have first column labeled "Dobropisujeme Vám" and 'faktura' - "Názov položky" + those columns are equal.
So I'm checking, if the type of Document is 'faktura' or 'dobropis' inside Table __init__ function and accordingly set
self.base_columns['column'].verbose_name = ...
The weird thing is that it works, but only after second refresh.
Situation: I've opened 'faktura' page - I can see 'Názov položky' which is ok. Then I open 'dobropis' page and I see 'Názov položky' label again. Then, if I refresh again, there is 'Dobropisujeme Vám'. Then, after each refresh, I see correct label - 'Dobropisujeme Vám'. But, if I open 'faktura' page, there is 'Dobropisujeme Vám' too for the first time, after second refresh it goes normal.
class PolozkyTable(tables.Table):
nazov_polozky = tables.columns.Column(orderable=False)
pocet = tables.columns.Column(orderable=False)
jednotka = tables.columns.Column(orderable=False)
cena = tables.columns.Column(orderable=False, verbose_name=u'Jednotková cena')
zlava = tables.columns.Column(orderable=False)
dph = tables.columns.Column(orderable=False)
celkom = tables.columns.Column(orderable=False)
class Meta:
model = Polozka
fields = ['nazov_polozky', 'pocet', 'jednotka', 'cena', 'zlava', 'dph', 'celkom']
attrs = {'class': 'table table-striped table-hover'}
def __init__(self, *args, **kwargs):
typ = kwargs.pop('typ')
super(PolozkyTable, self).__init__(*args, **kwargs)
if typ == 'dobropis':
self.base_columns['nazov_polozky'].verbose_name = u'Dobropisujeme Vám'
self.exclude = ['zlava', 'dph']
else:
self.base_columns['nazov_polozky'].verbose_name = u'Názov položky'
def render_cena(self, record):
return '{} {}'.format(record.cena, record.doklad.get_fakturacna_mena_display().encode('utf-8'))
def render_celkom(self, record):
return '{} {}'.format(record.celkom, record.doklad.get_fakturacna_mena_display().encode('utf-8'))
#login_required
def doklad_detail(request):
doklad = get_object_or_404(Doklad, pk=request.GET.get('id'))
polozky_table = PolozkyTable(doklad.polozky.all(),typ=doklad.typ)
return render(request, 'pdf/{}_pdf_template.html'.format(doklad.typ),
{'doklad': doklad, 'polozky_table': polozky_table})
There is no cache in this project and I really don't know what it could be.
Do you know?
EDIT:
Moreover, when I open 'faktura' after restarting the server, it shows camelcased 'Názov Položky' instead of 'Názov položky'. This is another weird thing because I searched whole project for case sensitive 'Názov Položky' and there is no such string.
EDIT2:
I've solved this creating separate table for 'dobropis' but I'm still curious which caused this problem. My colleague had the same problem on his PC.
You shouldn't be modifying base_columns in the __init__ method -- you are changing the value on the class, so it affects other views as well.
The base_columns have already been copied in the super() call before you alter them, so the changes only show up the next time you access the view.
It looks like you should be modifying self.columns instead. That should only affect the instance. Try setting header instead of verbose_name.
self.columns['nazov_polozky'].header = u'Dobropisujeme Vám'
Related
This is the thing, I was trying to make a crud for a rest api for a movie app, I was able to successfully perform the functionalities with flask to create a movie within the json, to delete a movie, and to list them, but I have problems with being able to edit one of the movies inside the JSON since I don't know how to access a sector of it to be able to modify it, this is what I was trying:
def __init__(self, idpelicula, nombre, descripcion, elenco, restriccionedad, fecha, calificacion):
self.idpelicula = idpelicula
self.nombre = nombre
self.descripcion = descripcion
self.elenco = elenco
self.restriccionedad = restriccionedad
self.fecha = fecha
self.calificacion = calificacion
movies_list = [
Peliculas(1, "Shrek", "Pelicula de Ogro", "Shrek, Fiona", "+APTA", "2001", "10"),
Peliculas(2, "Shrek2", "Otra Pelicula de Ogro", "Shrek, Fiona", "+APTA", "2005", "10"),
]
#app.route("/movies")
def list_movies():
return jsonify([pelicula.__dict__ for pelicula in movies_list])
#app.route("/movies/<id_search_movie>")
def obtener_pelicula(id_search_movie):
for pelicula in movies_list:
if pelicula.idpelicula == int(id_search_movie):
return jsonify(pelicula.__dict__)
return "Pelicula no encontrada", 404
#app.route("/movies", methods=['POST'])
def create_movie():
movie_data = request.get_json()
new_movie = Peliculas(movie_data["idpelicula"], movie_data["nombre"], movie_data["descripcion"],
movie_data["elenco"], movie_data["restriccionedad"], movie_data["fecha"],
movie_data["calificacion"])
movies_list.append(new_movie)
return "OK", 200
#app.route("/movies/<id_delete_movie>", methods=['DELETE'])
def delete_movie(id_delete_movie):
for pelicula in movies_list:
if pelicula.idpelicula == int(id_delete_movie):
movies_list.remove(pelicula)
return "OK", 200
This is what I dont know how to do, (I know its wrong, and that this isn't even the logic to do this), but this is what I tried:
#app.route("/movies", methods=['PUT'])
def edit_movie():
movie_data = request.get_json()
new_movie = Peliculas(movie_data["idpelicula"], movie_data["nombre"], movie_data["descripcion"],
movie_data["elenco"], movie_data["restriccionedad"], movie_data["fecha"],
movie_data["calificacion"])
movies_list.append(new_movie)
return "OK", 200
Any help, even from the point of view of the logic behind doing this will come handy. Thanks to you all.
There is a ton of problems with the way you are coding this at the moment, but here i will show you how you can edit an object in the way you are currently doing it.
So with your approach the edit view should be like this:
#app.route("/movies", methods=['PUT'])
def edit_movie():
# Accessing data from request
movie_data = request.get_json()
# Accesing Object from the movie-list by ID
for movie in movies_list:
if movie.idpelicula == movie_data["idpelicula"]:
edited_movie = movie
# Modifying the object attributes
for name, value in movie_data.items():
if hasattr(edited_movie, name):
setattr(edited_movie, name, value)
return "OK", 200
The problem here is that you were creating a new instance instead of editing an existing one.
in the future you could explore the usage of SQLAlchemy models so you can persist the data in a database, and still be managing objects.
PD1: Finally i recommend you be coherent with the naming, by this i mean if you are calling your variables and classes in spanish Pelicula, peliculas, try to use the same naming throught all your code, same if you use english.
PD2: Study some basic python usage of classes and variable assignment, so you can improve your code.
Greetings from Chile.
I am currently trying to change the name of the "Delete Selected" admin action. I have already effectively override the default (so I can store some data before completely deleting it), but now I want to change the option from the vague "Deleted selected" to something more specific like "Deleted all selected registrations." Or, at least, for it to say, "Deleted selected registrations" like it did before I overwrote the function.
I have so far tried this:
delete_selected.short_description = 'Delete all selected registrations'
But the option is still "Deleted selected." Is there a way to fix this?
Here's my code:
def delete_selected(modeladmin, request, queryset):
"""
This overrides the defult deleted_selected because we want to gather the data from the registration and create a
DeletedRegistration object before we delete it.
"""
for registration in queryset:
reg = registration.get_registrant()
if registration.payment_delegation:
delegate_name = registration.payment_delegation.name
delegate_email = registration.payment_delegation.email
else:
delegate_name = None
delegate_email = None
registration_to_delete = DeletedRegistration.objects.create(
registrant_name = reg.full_name(),
registrant_email = reg.email,
registrant_phone_num = reg.phone,
delegate_name = delegate_name,
delegate_email = delegate_email,
# Filtering out people (with True) who couldn't participate in events because we are only interested in the people
# we had to reserve space and prepare materials for.
num_of_participants = registration.get_num_party_members(True),
special_event = registration.sibs_event,
)
registration.delete()
delete_selected.short_description = 'Delete all selected registrations'
edit: just tried delete_selected.list_display that didn't work either
You can't have it in the function, so I just had to tab it back one space and it worked.
example:
def delete_selected(modeladmin, request, queryset)
code
delete_selected.short_description = "preferred name"
thanks.
I have a PublicationsPage and a PublicationPage class.
The PublicationsPage shows a list of its direct children and renders them in a short preview list at http://mysite/publications.
class PublicationsPage(Page):
# ...
def get_context(self, request):
context = super().get_context(request)
publications = PublicationPage.objects.child_of(self).live()
context['publications'] = publications
return context
This means whenever a new PublicationPage is added/deleted/modified the list updates accordingly. But since I am not updating the PublicationsPage the lastmod/last_published_at attribute of the /publications location never changes. Wouldn't this be missleading for a search engine?
A really hacky attempt could be to update the last_published_at date of the parent page every time I touch a child entry.
class PublicationPage(Page):
# ...
def save(self, *args, **kwargs):
result = super().save(*args, **kwargs)
from datetime import datetime
parent_page = self.get_parent()
parent_page.last_published_at = datetime.now()
parent_page.save()
return result
Any other suggestions?
When generating the sitemap, you can set the property lastmod, which accepts a method, called for every item of the sitemap.
Therefore, when generating the PublicationsPage sitemap, set that property to a method that queries all the children of each PublicationsPage item and return the latest date.
You can define get_sitemap_urls on your Page model, something like this:
class PublicationsPage(Page):
def get_sitemap_urls(self, request=None):
# Get the defaults sitemap URLS.
urls = super().get_sitemap_urls(request=request)
# Get the last modifications time of your publications.
publications = PublicationPage.objects.child_of(self).live()
last_publication = publications.order_by('-last_published_at').first()
# Update the default entry.
if last_publication:
urls[0]['lastmod'] = last_publication.last_published_at
# Return the urls.
return urls
I have this Class in a project, and I'm trying to get previous and next elements of current one.
def get_context(self, request, *args, **kwargs):
context = super(GuidePage, self).get_context(request, *args, **kwargs)
context = get_article_context(context)
all_guides = GuidePage.objects.all().order_by("-date")
context['all_guides'] = all_guides
context['next_guide'] = all_guides.filter(date__lt=self.date)
context['prev_guide'] = all_guides.filter(date__gt=self.date)
print context['next_guide']
print context['prev_guide']
return context
These two lines:
context['prev_guide'] = all_guides.filter(date__lt=self.date)
context['next_guide'] = all_guides.filter(date__gt=self.date)
are returning empty results as printed in the console:
(QuerySet[])
(QuerySet[])
What am I missing?
EDIT:
I changed lt and gt to lte and gte. As I understand that will include results that are also equal in date.
In this case I got ALL elements. All elements were created the same day, but, of course, at different times, so they should be different by minutes. Is this difference not taking into account when filtering for greater/lesser ?
If you want to filter not only by date, but time also, you must change the relevant field in your model to be of DateTimeField instead of DateField.
Like this:
from django.db import models
class MyModel(models.Model):
date_time = models.DateTimeField()
Now, you can do stuff like all_guides.filter(date_time__lte=self.date_time) or all_guides.filter(date_time__gte=self.date_time).
Carefull of the two underscores __.
My general question is: can I use the data stored in a HStoreField (Django 1.8.9) to generate columns dynamically for an existing Table class of django-tables2? As an example below, say I have a model:
from django.contrib.postgres import fields as pgfields
GameSession(models.Model):
user = models.ForeignKey('profile.GamerProfile')
game = models.ForeignKey('games.Game')
last_achievement = models.ForeignKey('games.Achievement')
extra_info = pgfields.HStoreField(null=True, blank=True)
Now, say I have a table defined as:
GameSessionTable(tables.Table):
class Meta(BaseMetaTable):
model = GameSession
fields = []
orderable=False
id = tables.LinkColumn(accessor='id', verbose_name='Id', viewname='reporting:session_stats', args=[A('id')], attrs={'a':{'target':'_blank'}})
started = DateTimeColumn(accessor='startdata.when_started', verbose_name='Started')
stopped = DateTimeColumn(accessor='stopdata.when_stopped', verbose_name='Stopped')
game_name = tables.LinkColumn(accessor='game.name', verbose_name='Game name', viewname='reporting:game_stats', args=[A('mainjob.id')], attrs={'a':{'target':'_blank'}})
I want to be able to add columns for each of the keys stored in the extra_info column for all of the GameSessions. I have tried to override the init() method of the GameSessionTable class, where I have access to the queryset, then make a set of all the keys of my GameSession objects, then add them to self, however that doesn't seem to work. Code below:
def __init__(self, data, *args, **kwargs):
super(GameSessionTable, self).__init__(data, *args, **kwargs)
if data:
extra_cols=[]
# just to be sure, check that the model has the extra_info HStore field
if data.model._meta.get_field('extra_info'):
extra_cols = list(set([item for q in data if q.extra_info for item in q.extra_info.keys()]))
for col in extra_cols:
self.columns.columns[col] = tables.Column(accessor='extra_info.%s' %col, verbose_name=col.replace("_", " ").title())
Just a mention, I have had a look at https://spapas.github.io/2015/10/05/django-dynamic-tables-similar-models/#introduction but it's not been much help because the use case there is related to the fields of a model, whereas my situation is slightly different as you can see above.
Just wanted to check, is this even possible or do I have to define an entirely different table for this data, or potentially use an entirely different library altogether like django-reports-builder?
Managed to figure this out to a certain extent. The code I was running above was slightly wrong, so I updated it to run my code before the superclass init() gets run, and changed where I was adding the columns.
As a result, my init() function now looks like this:
def __init__(self, data, *args, **kwargs):
if data:
extra_cols=[]
# just to be sure, check that the model has the extra_info HStore field
if data.model._meta.get_field('extra_info'):
extra_cols = list(set([item for q in data if q.extra_info for item in q.extra_info.keys()]))
for col in extra_cols:
self.base_columns[col] = tables.Column(accessor='extra_info.%s' %col, verbose_name=col.replace("_", " ").title())
super(GameSessionTable, self).__init__(data, *args, **kwargs)
Note that I replaced self.columns.columns (which were BoundColumn instances) with self.base_columns. This allows the superclass to then consider these as well when initializing the Table class.
Might not be the most elegant solution, but it seems to do the trick for me.