I have a function, that if i run from django shell, it populates my db succesfully.
I have three classes.
Class League(models.Model):
LeagueName = models.CharField()
#class League explained below
Class Fixture(models.Model):
League = models.ForeignKey(League)
home_team = models.ForeginKey(Team)
away_team = models.ForeginKey(Team)
Class Teams(models.Model):
League = models.ForeignKey(League)
I want the functionality to able to calculate the fixture table with respect to only one league. Here is what I am doing now. which at the moment it is not doing. How to?
class League(models.Model):
LeagueName = models.CharField(max_length=200)
FixturesGenerated = models.BooleanField(default=False)
def __unicode__(self):
return self.LeagueName
def CreateFixtures(self, print_only=True):
if self.FixturesGenerated==True:
return
from dateutil import rrule
from dateutil.relativedelta import *
from League.models import Team, Fixture
import itertools
import datetime
import random
"""
Instead of your array I will use actual objects from the Teams model
"""
teams = Team.objects.all()
fixcombos = list(itertools.combinations(teams,2))
random.shuffle(fixcombos)
nofixtures = len(fixcombos)
datestart = datetime.date.today()
dateend = datestart + datetime.timedelta(days=125)
#calculate possible fixture dates,
fixdays = list(rrule.rrule(rrule.DAILY, byweekday=(rrule.SA,rrule.SU), dtstart=datestart, until=dateend))
nofmatchdays = len(fixdays)
# perday = perday matches, and then moved it into array for for loop of dates available.
perday = nofixtures/nofmatchdays +1
perday = range(0,perday)
#for loop to extend the fixture days array to repeat dates.
for x in perday:
fixdays = fixdays + fixdays
fixarray = range(0, nofixtures)
# for loop for printing the possible functions
# this for loop number will give the database entry id number of a particular name. we still have to do some manipulation.
result = ''
for y in fixarray:
printline = 'Date: ' + str(fixdays[y]) + ': Teams Involved: ' + str(fixcombos[y])
result += printline
# now the print array functionality needs to be replaced with p.save() functionality in the final program.
"""
Code to actually save the fixture if print_only is not True
"""
if not print_only:
f = Fixture()
f.league = self
f.fixture_date = fixdays[y]
f.team_one = fixcombos[y][0]
f.team_two = fixcombos[y][1]
f.save()
self.FixturesGenerated = True
self.save()
[EDIT] To furthur elaborate, here is my admin.py
from League.models import League
from League.models import Team
from League.models import Fixture
from django.contrib import admin
from django.http import HttpResponseRedirect
class ButtonableModelAdmin(admin.ModelAdmin):
buttons=()
def change_view(self, request, object_id, extra_context={}):
extra_context['buttons']=self.buttons
return super(ButtonableModelAdmin, self).change_view(request, object_id, extra_context)
def button_view_dispatcher(self, request, object_id, command):
obj = self.model._default_manager.get(pk=object_id)
return getattr(self, command)(request, obj) \
or HttpResponseRedirect(request.META['HTTP_REFERER'])
def get_urls(self):
from django.conf.urls.defaults import patterns, url
from django.utils.functional import update_wrapper
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.module_name
return patterns('',
*(url(r'^(\d+)/(%s)/$' % but[0], wrap(self.button_view_dispatcher)) for but in self.buttons)
) + super(ButtonableModelAdmin, self).get_urls()
class TeamsInLeague(admin.StackedInline):
model = Team
extra = 1
class FixturesInLeague(admin.TabularInline):
model = Fixture
extra = 0
class LeagueAdmin(ButtonableModelAdmin):
fields = ['LeagueName', 'FixturesGenerated']
inlines = [TeamsInLeague, FixturesInLeague, ]
def gen_fixtures(self, request, obj):
obj.CreateFixtures(print_only=False)
gen_fixtures.short_description = 'Generate Fixtures'
gen_fixtures.url = "gen_fixtures"
buttons = [ (gen_fixtures.func_name, gen_fixtures.short_description) ]
admin.site.register(League,LeagueAdmin)
admin.site.register(Team)
admin.site.register(Fixture)
and here it is from my templates/../change_form.html.
{% extends "admin/change_form.html" %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
{% for button in buttons %}
<li>{{ button.1 }}</li>
{% endfor %}
<li>History ala bala</li>
{% if has_absolute_url %}<li>View on site</li>{% endif%}
</ul>
{% endif %}{% endif %}
{% endblock %}
thankyou.
//mouse
My suggestion (after doing my best to understand what you are trying to do) is to make your anchors use a get parameter of your team id.
{% for button in buttons %}
<li>{{ button.1 }}</li>
{% endfor %}
And then in your view you could pull off the team ID from the request and call CreateFixtures.
team = request.get('team_id')
league.CreateFixtures(team, print_only=False)
Thats the best I can do based on the code you pasted. I strongly suggest you rework some of it to be more readable.
Related
So,how get data from django database?I have django models and created 2 objects there.I want to get 2 objects and write in html file. admin panel: https://i.stack.imgur.com/7WUZr.png
view.py
def vds(request):
HTML_STRING = render_to_string("vds.html", context=context1)
return HttpResponse(HTML_STRING)
VDSTARIFS_obj = VDSTARIFS.objects.get(id=1)
context1 = {
"prise": VDSTARIFS_obj.prise,
}
file with models
class VDSTARIFS( models.Model):
prise = models.CharField(max_length= 10,)
def __str__(self):
return str(self.prise)```
Referring to the Django docs:
https://docs.djangoproject.com/en/3.2/intro/tutorial03/#a-shortcut-render
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
Edited for your model
from django.shortcuts import render
from .models import VDSTARIFS
def vds(request):
ALL_VDSTARIFS = VDSTARIFS.objects.all()
FIRST_VDSTARIF = ALL_VDSTARIFS[:1]
# Get prise
prise = FIRST_VDSTARIF.prise
# Alternatives to print 'prise' in template
# Pass only the first item of vds list
context = {
"FIRST_VDSTARIF": FIRST_VDSTARIF
}
# Pass prise only
context = {
"prise": prise
}
# All records in html also the needed one in separated item
context = {
"ALL_VDSTARIFS": VDSTARIFS,
"FIRST_VDSTARIF": FIRST_VDSTARIF
}
return render(request, 'vds.html', context)
}
In template you can use if
https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#if
{# Runs along all tarifs and print only one, not good. #}
{% for vdstarif in ALL_VDSTARIFS %}
{% if vdstarifs.id == "1" %}
{{ vdstarif.prise }}
{% endif %}
{% endfor %}
{# Only print the needed one #}
{{ FIRST_VDSTARIF.prise }}
{# Print prise #}
{{ prise }}
You might follow python and django coding conventions and my suggestions.
Rename VDSTARIFS -> VdsTarif (look at your admin page, Django adds 's' to your model to make it plural)
Use singular names for your models and add a suffix to variable names in views like vdstarif_list.
For "prise" field use DecimalField, looks like it is a CharField. Is it prise or price?
https://docs.djangoproject.com/en/3.2/ref/models/fields/#decimalfield
from django.shortcuts import render
from .models import VdsTarif
def vds(request):
vdstarif_list = VdsTarif.objects.all()
context = {
"vdstarif_list" : vfstarif_list[:0].prise
}
return render(request, 'vds.html', context)
I wanted to follow the methodology in Python Crash Course Ch. 18 on building the learning_logs app to build the pizzas app. However I'm stuck at displaying the pizzas' name in pizzas.html.
There should be 2 pizzas, named "Hawaiian" and "Meat Lovers". I added both using the admin account. Checked through the shell that both are stored in Pizza.objects.all() so I guess it's the problem to recall them.
Some codes for you all's reference:
views.py:
from django.shortcuts import render
from .models import Pizza
# Create your views here.
def index(request):
""" The home page for Pizzeria. """
return render(request, "pizzas/index.html")
def pizzas(request):
""" Show all pizzas available. """
pizzas = Pizza.objects.all()
content = {"pizza": pizzas}
return render(request, "pizzas/pizzas.html", content)
models.py
from django.db import models
# Create your models here.
class Pizza(models.Model):
""" Pizza available. """
name = models.CharField(max_length = 50)
def __str__(self):
""" Return a string representation of the pizza name. """
return self.name
class Topping(models.Model):
""" The toppoings on the pizza. """
pizza = models.ForeignKey(Pizza, on_delete = models.CASCADE)
text = models.CharField(max_length = 250, unique = True)
def __str__(self):
""" Return a string representation of the toppings. """
return self.text
urls.py:
from django.urls import path, include
from . import views
app_name = "pizzas"
urlpatterns = [
# Home page
path("", views.index, name = "index"),
# Show all pizzas.
path("pizzas/", views.pizzas, name = "pizzas"),
]
pizzas.html:
{% extends 'pizzas/base.html' %}
{% block content %}
<p>Pizzas</p>
<ul>
{% for pizza in pizzas %}
<li>{{ pizza }}</li>
{% empty %}
<li>No pizza duh.</li>
{% endfor %}
</ul>
{% endblock content %}
Expected to see both "Hawaiian" and "Meat Lovers" appears in the list under "Pizzas" in localhost:8000/pizzas.html but instead shows "no pizza duh". What do I miss?
I did see changing the relationship to both models would help solve the problem but if I don't, how can I change the other parts to get what I expect?
You don't have anything called pizzas in your template context; you named the variable pizza.
You should change the context to use pizzas:
content = {"pizzas": pizzas}
The admin actions seem to work on several items selected in the list view of django admin interface:
In my case I would like to have a simple action button on the change (one item) view.
Is there a way to make the django admin actions available there?
I know that I can walk around this problem by going to the list view, and select one item there. But it would be more nice to have it directly available.
Create a template for your model in your app.
templates/admin/<yourapp>/<yourmodel>/change_form.html
With this example content to add a button when changing an existing object.
{% extends "admin/change_form.html" %}
{% block submit_buttons_bottom %}
{{ block.super }}
{% if original %} {# Only show if changing #}
<div class="submit-row">
<a href="{% url 'custom-model-action' original.pk %}">
Another action
</a>
</div>
{% endif %}
{% endblock %}
Link that action to any url and redirect back to your model change object view. More information about extending admin templates.
Update: Added complete common use case for custom action on existing object
urls.py
urlpatterns = [
url(r'^custom_model_action/(?P<object_pk>\d+)/$',
core_views.custom_model_action, name='custom-model-action')
]
views.py
from django.urls import reverse
from django.contrib import messages
from django.http import HttpResponse, HttpResponseRedirect
def custom_model_action(request, object_pk):
messages.info(request, 'Performed custom action!')
return HttpResponseRedirect(
reverse('admin:<yourapp>_<yourmodel>_change', args=[object_pk])
)
If you realy need per-single object, I suggest you to use this solution, eg:
class Gallery(TimeStampedModel):
title = models.CharField(max_length=200)
attachment = models.FileField(upload_to='gallery/attachment/%Y/%m/%d')
def __str__(self):
return self.title
def process_button(self):
return ('<button id="%(id)s class="btn btn-default process_btn" '
'data-value="%(value)s>Process</button>' % {'id': self.pk, 'value': self.attachment.url})
process_button.short_description = 'Action'
process_button.allow_tags = True
In your admin.py, insert process_button into list_display;
class GalleryAdmin(admin.ModelAdmin):
list_display = ['title', 'process_button', 'created']
search_fields = ['title', 'pk']
....
class Media:
js = ('path/to/yourfile.js', )
Then, inside yourfile.js, you can also process it..
$('.process_btn').click(function(){
var id = $(this).attr('id'); // single object id
var value = $(this).data('value'); // single object value
...
});
Hope it helpful..
Not the same as the topic starter asked, but this snippet allows to have Single Object action from on the list page with minimum amount of code
BaseAction code
class AdminActionError(Exception):
pass
class AdminObjectAction:
"""Base class for Django Admin actions for single object"""
short_description = None
exp_obj_state = {}
def __init__(self, modeladmin, request, queryset):
self.admin = modeladmin
self.request = request
self.queryset = queryset
self.__call__()
def validate_qs(self):
count = self.queryset.count()
if count != 1:
self.error("You must select one object for this action.")
if self.exp_obj_state:
if self.queryset.filter(**self.exp_obj_state).count() != 1:
self.error(f'Selected object does not meet the requirements: {self.exp_obj_state}')
def error(self, msg):
raise AdminActionError(msg)
def get_object(self):
return self.queryset.get()
def process_object_action(self, obj):
pass
def validate_obj(self, obj):
pass
def __call__(self, *args, **kwargs):
try:
self.validate_qs()
obj = self.get_object()
self.validate_obj(obj)
except AdminActionError as e:
self.admin.message_user(self.request, f"Failed: {e}", level=messages.ERROR)
else:
with transaction.atomic():
result = self.process_object_action(obj)
self.admin.message_user(self.request, f"Success: {self.short_description}, {result}")
Custom Action [minimum amount of code]
class RenewSubscriptionAction(AdminObjectAction):
short_description = 'Renew subscription'
exp_obj_state = {
'child': None,
'active_status': True,
}
def process_object_action(self, obj):
manager = RenewManager(user=obj.user, subscription=obj)
return manager.process()
AdminClass
class SomeAdmin(admin.ModelAdmin):
actions = [RenewSubscriptionAction]
The built-in admin actions operate on a queryset.
You can use a calable for the action you whant or to show something else:
class ProductAdmin(admin.ModelAdmin):
list_display ('name' )
readonly_fields('detail_url)
def detail_url(self, instance):
url = reverse('product_detail', kwargs={'pk': instance.slug})
response = format_html("""{0}""", product_detail)
return response
or using forms
class ProductForm(forms.Form):
name = forms.Charfield()
def form_action(self, product, user):
return Product.value(
id=product.pk,
user= user,
.....
)
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
# render buttons and links to
def product_actions(self, obj):
return format_html(
'<a class="button" href="{}">Action1</a> '
'<a class="button" href="{}">Action 2</a>',
reverse('admin:product-action-1', args=[obj.pk]),
reverse('admin:aproduct-action-3', args=[obj.pk]),
)
for more details about using forms
What I am trying to do is the following: User will have a production (also known as podcast episode) already created with the necessary info until this point (production_id would be the id for this query). The idea is, when user arrives to ChapterMark template, he would be able to create several timestamps to point out certain topics he/she is talking throughout his/her episode. chaptermark_id is created since it would be a One-To-Many and with this id I can add as much timestamps as I want within that episode. With this in mind, which is the best approach for this type of situation and how I can implement it in my form, class view and template?
Thanks in advance
Here is my views.py:
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden
from django.shortcuts import render, get_object_or_404
from django.views.generic import View, RedirectView, TemplateView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms.client_setup import ClientSetupForm
from .forms.podcast_setup import PodcastSetupForm
from .forms.episode_info import EpisodeInfoForm
from .forms.image_files import EpisodeImageFilesForm
from .forms.wordpress_info import EpisodeWordpressInfoForm
from .forms.chapter_marks import EpisodeChapterMarksForm
from .forms.show_links import ShowLinksForm
from .forms.tweetables import TweetablesForm
from .forms.clicktotweet import ClickToTweetForm
from .forms.schedule import ScheduleForm
from .forms.wordpress_account import WordpressAccountForm
from .forms.wordpress_account_setup import WordpressAccountSetupForm
from .forms.wordpress_account_sortable import WordpressAccountSortableForm
from .forms.soundcloud_account import SoundcloudAccountForm
from .forms.twitter_account import TwitterAccountForm
from producer.helpers import get_podfunnel_client_and_podcast_for_user
from producer.helpers.soundcloud_api import SoundcloudAPI
from producer.helpers.twitter import TwitterAPI
from django.conf import settings
from producer.models import Client, Production, ChapterMark, ProductionLink, ProductionTweet, Podcast, WordpressConfig, Credentials, WordPressSortableSection, \
TwitterConfig, SoundcloudConfig
from django.core.urlresolvers import reverse
from producer.tasks.auphonic import update_or_create_preset_for_podcast
class EpisodeChapterMarksView(LoginRequiredMixin, View):
form_class = EpisodeChapterMarksForm
template_name = 'fc/forms_chapter_marks.html'
def get(self, request, *args, **kwargs):
initial_values = {}
user = request.user
# Lets get client and podcast for the user already. if not existent raise 404
client, podcast = get_fc_client_and_podcast_for_user(user)
if client is None or podcast is None:
raise Http404
# The production_id or the chaptermark_id must be passed on teh KWargs
production_id = kwargs.get('production_id', None)
chaptermark_id = kwargs.get('chaptermark_id', None)
if chaptermark_id:
chaptermark = get_object_or_404(ChapterMark, id=chaptermark_id)
production = chaptermark.production
elif production_id:
production = get_object_or_404(Production, id=production_id)
chaptermark = None
initial_values['production_id'] = production.id
if chaptermark is not None:
initial_values['chaptermark_id'] = chaptermark_id
initial_values['start_time'] = chaptermark.start_time
initial_values['title'] = chaptermark.title
form = self.form_class(initial=initial_values)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# lets get the data
production_id = form.cleaned_data.get('production_id')
chaptermark_id = form.cleaned_data.get('chaptermark_id')
start_time = form.cleaned_data.get('start_time')
title = form.cleaned_data.get('title')
# Get production
production = get_object_or_404(Production, id=production_id)
# if a chaptermark existed, we update, if not we create
if chaptermark_id is not None:
chaptermark = ChapterMark.objects.get(id=chaptermark_id)
else:
chaptermark = ChapterMark()
chaptermark.start_time = start_time
chaptermark.title = title
chaptermark.production = production
chaptermark.save()
return HttpResponseRedirect(reverse('fc:episodeshowlinks'))
return render(request, self.template_name, {'form': form})
chaptermark.py form:
from django import forms
class EpisodeChapterMarksForm(forms.Form):
production_id = forms.IntegerField(widget=forms.Field.hidden_widget, required=False)
chaptermark_id = forms.IntegerField(widget=forms.Field.hidden_widget, required=False)
start_time = forms.TimeField(required=False)
title = forms.CharField(max_length=200)
chaptermark template:
{% extends "fc/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-success active" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%">
<span class="sr-only">50% Complete</span>
</div>
</div>
<div class="panel panel-default box-shadow--16dp col-sm-6 col-sm-offset-3">
<div class="panel-body">
<div class='row'>
<div class='col-sm-12'>
{% if title %}
<h1 class='{% if title_align_center %}text-align-center{% endif %}'>{{ title }}<!-- : {{ get.clientsetup.company_name }} --></h1>
{% endif %}
{% if subtitle %}
<h3 class='{% if subtitle_align_center %}text-align-center{% endif %}'>{{ subtitle }}</h4>
{% endif %}
<h5>Chapter Marks</h5>
<form method='POST' action=''>{% csrf_token %}
{{ form|crispy }}
<hr/>
<button type="submit" class="btn btn-primary box-shadow--6dp"><i class="fa fa-chevron-right pull-right"></i> Continue
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
----------------------UPDATE-------------------------
Was in views.py:
#login_required
def episodechaptermarks(request):
title = 'Podcast'
title_align_center = True
subtitle = 'Setup | Add Episode'
subtitle_align_center = True
form = ChapterMarksForm(request.POST or None)
context = {
"title": title,
"subtitle": subtitle,
"form": form
}
if form.is_valid():
instance = form.save(commit=False)
start_time = form.cleaned_data.get("start_time")
title = form.cleaned_data.get("title")
instance.start_time = start_time
instance.title = title
instance.user = request.user
instance.save()
return render(request, "pod_funnel/forms_chapter_marks.html", context)
else:
return render(request, "pod_funnel/forms_chapter_marks.html", context)
ModelForm:
from django import forms
from producer.models import ChapterMark
class ChapterMarksForm(forms.ModelForm):
class Meta:
model = ChapterMark
fields = ['start_time', 'title']
def clean_start_time(self):
start_time = self.cleaned_data.get('start_time')
return start_time
def clean_title(self):
title = self.cleaned_data.get('title')
return title
In essence, your production object has a series of timestamps that relate back via a FK. You need a set of views for CRUD at the production level. Let's assume your models are already created. There's a few things from my experience I want to point out that I think will point you in the right direction.
Unless absolutely necessary never use a Form class when creating a form object that mirrors a model; you are introducing a need for unnecessary complexity and opening the door for errors. Use a ModelForm, which can save objects to the DB straight from the view and help you manage cleaning, validation, and more. In addition, these can easily mesh with generic views of all sorts.
For this sort of relation (a model object with a varying number of model objects of a given type relating back that object) Django provides the powerful but difficult inlineformset_factory. This creates a series of inline forms as needed for a relation such as this.
So you have a model (production) and another related back to that (timestamp). You need to save these at the same time, possibly perform cleaning or validation, and really provide CRUD functionality for this relationship as a whole. For this, you could create a complex view from scratch or you could use django-extra-views and their generic CBVs for models with inlines. You can subclass CreateWithInlinesView, UpdateWithInlinesView. Why? Most Django devs would agree formsets are difficult to implement.
So, to give you a simplified version of how you can do this
from extra_views.advanced import CreateWithInlinesView, InlineFormSet, UpdateWithInlinesView
class TimeStampsInline(InlineFormSet):
model = models.TimeStamp
form = TimeStampForm # If you haven't created a custom ModelForm, can also specify "fields= ['field_1','field_2',...] and the CBV will create a ModelForm
extra = 0
class ProductionCreate(CreateWithInlinesView):
model=models.Production
inlines = [TimeStampsInline]
success_url = reverse('production-list') # the url to return to on successful create
exclude = ['created_by'] # render all fields except this in form
template_name = 'myapp/update.html'
class ProductionUpdate(UpdateWithInlinesView):
model=models.Production
inlines = [TimeStampsInline]
success_url = reverse('production-list')
exclude = ['created_by']
template_name = 'myapp/update.html'
Your template(s) will have to be built in specification with formsets; there's documentation and tutorials all over for that.
That's already a lot to digest, but you probably get the general idea. Don't build a horse from scratch ;)
I am using the https://github.com/bendavis78/django-admin-csv to export records to csv. But it will download all records even though I select only a few.
from admin_csv import CSVMixin
class MyModelAdmin(CSVMixin, admin.ModelAdmin):
list_display = ['foo', 'bar', 'baz']
csv_fields = list_display + ['qux']
Here is the admin.py file
from functools import update_wrapper
from django.contrib.admin.utils import label_for_field
class CSVMixin(object):
"""
Adds a CSV export action to an admin view.
"""
change_list_template = "admin/change_list_csv.html"
# This is the maximum number of records that will be written.
# Exporting massive numbers of records should be done asynchronously.
csv_record_limit = None
csv_fields = []
csv_headers = {}
def get_csv_fields(self, request):
return self.csv_fields or self.list_display
def get_urls(self):
from django.conf.urls import url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
opts = self.model._meta
urlname = '{0.app_label}_{0.model_name}_csvdownload'.format(opts)
urlpatterns = [
url('^csv/$', wrap(self.csv_export), name=urlname)
]
return urlpatterns + super(CSVMixin, self).get_urls()
def get_csv_filename(self, request):
return unicode(self.model._meta.verbose_name_plural)
def changelist_view(self, request, extra_context=None):
context = {
'querystring': request.GET.urlencode()
}
context.update(extra_context or {})
return super(CSVMixin, self).changelist_view(request, context)
def csv_header_for_field(self, field_name):
if self.headers.get(field_name):
return self.headers[field_name]
return label_for_field(field_name, self.model, self)
def csv_export(self, request, *args, **kwargs):
import csv
from django.http import HttpResponse
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = (
'attachment; filename={0}.csv'.format(
self.get_csv_filename(request)))
fields = list(self.get_csv_fields(request))
writer = csv.DictWriter(response, fields)
# Write header row.
headers = dict((f, self.csv_header_for_field(f)) for f in fields)
writer.writerow(headers)
# Get the queryset using the changelist
cl_response = self.changelist_view(request)
cl = cl_response.context_data.get('cl')
queryset = cl.get_queryset(request)
# Write records.
if self.csv_record_limit:
queryset = queryset[:self.csv_record_limit]
for r in queryset:
data = {}
for name in fields:
if hasattr(r, name):
data[name] = getattr(r, name)
elif hasattr(self, name):
data[name] = getattr(self, name)(r)
else:
raise ValueError('Unknown field: {}'.format(name))
if callable(data[name]):
data[name] = data[name]()
writer.writerow(data)
return response
csv_export.short_description = \
'Exported selected %(verbose_name_plural)s as CSV'
Here is the change_list_csv.html.
{% extends "admin/change_list.html" %}
{% load admin_urls %}
{% block object-tools-items %}
{{ block.super }}
<li>
{% url cl.opts|admin_urlname:'csvdownload' as download_url %}
Download CSV
</li>
{% endblock %}
The file looks simple but can't figure out what to change to only export the selected rows.
I think the problem is your "opts = self.model._meta". When I export to csv using a custom admin action, I use this: opts = queryset.model._meta, and the admin queryset does the limiting for me. The structure of your code is very different from mine so I don't know how you need to get the equivalent to the "queryset", but I think that is what is missing or not functioning correctly.