I am trying to build a dashboard type site with multiple charts. I am using Django with FusionCharts and a Postregsql database backend. I am able to get one chart to render, but I can't get a second one to appear at all. I think it is probably something in my views.py with how I am creating the functions. Any help is much appreciated.
Code is as follows:
views.py
from django.shortcuts import render
from django.http import HttpResponse
# Include the `fusioncharts.py` file that contains functions to embed the charts.
from .fusioncharts import FusionCharts
from .models import *
# The `chart` function is defined to load data from a `Country` Model.
# This data will be converted to JSON and the chart will be rendered.
def chart(request):
# Chart data is passed to the `dataSource` parameter, as dict, in the form of key-value pairs.
dataSource = {}
dataSource['chart'] = {
"caption": "Final Sale Price by Customer",
"showValues": "0",
"theme": "fint"
}
dataSource['data'] = []
for key in Customer.objects.all():
data = {}
data['label'] = key.customername
data['value'] = key.Final_Price
dataSource['data'].append(data)
column2D = FusionCharts("column2D", "ex1", "600", "350", "chart-1", "json", dataSource)
return render(request, 'dashboard.html', {'output': column2D.render()})
def chart2(request):
# Chart data is passed to the `dataSource` parameter, as dict, in the form of key-value pairs.
dataSource2 = {}
dataSource2['chart'] = {
"caption": "Final Sale Price by Plant",
"showValues": "0",
"theme": "fint"
}
dataSource2['data'] = []
for key in Customer.objects.all():
data = {}
data['label'] = key.customername
data['value'] = key.Final_Price
dataSource2['data'].append(data)
column2D = FusionCharts("column2D", "ex1", "600", "350", "chart-2", "json", dataSource2)
return render(request, 'dashboard.html', {'output2': column2D.render()})
dashboard.html
{% extends "index.html" %}
{% load static %}
{% block title %}{{title}}{% endblock title %}
{% block sidenav %}
{% for page in page_list %}
<li>
{{page.title}}
</li>
{% endfor %}
{% endblock sidenav %}
{% block content %}
{% autoescape off %}
{{ content }}
{% endautoescape %}
<p>
<table>
<tr>
<th>Customer</th>
<th>Plant</th>
</tr>
<tr>
<td><div id="chart-1">{{ output|safe }}</div></td>
<td><div id="chart-2">{{ output|safe }}</div><h1>test</h1></td>
</tr>
</table>
Page last Update: {{last_updated|date:'D d F Y' }}
</p>
{% endblock content %}
manage.py
from django.db import models
class Customer(models.Model):
customername = models.CharField(max_length=250)
Final_Price = models.CharField(max_length=50)
def __unicode__(self):
return u'%s %s' % (self.customername, self.Final_Price)
class Plant(models.Model):
site = models.CharField(max_length=250)
Final_Price = models.CharField(max_length=50)
def __unicode__(self):
return u'%s %s' % (self.site, self.Final_Price)
I ended up figuring it out. It turns out there were a boatload of problems in the previous code. I figured I'd post it as a reference for someone having the same question in the future. The code that is working is as follows:
views.py
from django.shortcuts import render
from django.http import HttpResponse
# Include the `fusioncharts.py` file that contains functions to embed the charts.
from .fusioncharts import FusionCharts
from .models import *
# The `chart` function is defined to load data from a `Country` Model.
# This data will be converted to JSON and the chart will be rendered.
def chart(request):
# Customer
dataSource = {}
dataSource['chart'] = {
"caption": "Final Sale Price by Customer",
"showValues": "0",
"theme": "carbon"
}
dataSource['data'] = []
for key in Customer.objects.all():
data = {}
data['label'] = key.customername
data['value'] = key.Final_Price
dataSource['data'].append(data)
plantdataSource = {}
plantdataSource['chart'] = {
"caption": "Final Sale Price by Plant",
"showValues": "0",
"theme": "carbon"
}
plantdataSource['data'] = []
for key in Plant.objects.all():
data = {}
data['label'] = key.site
data['value'] = key.Final_Price
plantdataSource['data'].append(data)
colchart = FusionCharts("column2D", "ex1", "1000", "350", "chart-1", "json", dataSource)
plantchart = FusionCharts("column2D", "ex2", "1000", "350", "chart-2", "json", plantdataSource)
return render(request, 'dashboard.html', {'output': colchart.render(), 'output2': plantchart.render()})
dashboard.html
{% extends "index.html" %}
{% load static %}
{% block title %}{{title}}{% endblock title %}
{% block sidenav %}
{% for page in page_list %}
<li>
{{page.title}}
</li>
{% endfor %}
{% endblock sidenav %}
{% block content %}
{% autoescape off %}
{{ content }}
{% endautoescape %}
<p>
<table>
<tr>
<th>Customer</th>
<th>Plant</th>
</tr>
<tr>
<td><div id="chart-1">{{ output|safe }}</div></td>
<td><div id="chart-2">{{ output2|safe }}</div></td>
</tr>
</table>
Page last Update: {{last_updated|date:'D d F Y' }}
</p>
{% endblock content %}
You can just split the screen in HTML, and it will work as below
{{ chartTitle|safe }}
<div style="width: 100%; overflow:auto;">
<div style="float:left; width: 50%">
<div id="chart-1" style="width: 50%;flaot: left;">{{ output|safe }}</div>
</div>
<div style="float:right; width: 50%">
<div id="chart-2">{{ pie_output|safe }}</div>
</div>
</div>
<br/>
Related
So I got a QuerySet result from an aggregate function to display in a low-spec eBay clone of mine. But my problem is displaying certain fields in the Django template as I want, for example, I want to display the highest bidder's username and bid. When I call the whole object itself like so {{winner}}, then it displays. But when I try to access its fields like so {{winner.user_id.username}}, I get no output although the QuerySet does execute.
When I try to get a field like so (winner.user_id.username):
When I only call winner:
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
CATEGORIES = [
('Appliances', 'Appliances'),
('Tech', 'Tech'),
('Gaming', 'Gaming'),
('Fashion', 'Fashion'),
('Sports and Fitness','Sports and Fitness'),
('Other','Other'),
("Hygiene and Medicine","Hygiene and Medicine"),
("Stationery","Stationery"),
('Decor', 'Decor'),
('Furniture','Furniture'),
('Cars and Mechanical Things','Cars and Mechanical Things'),
("Tools","Tools")
]
# Create models here
class User(AbstractUser):
pass
class Auction_Listing(models.Model):
user_id = models.IntegerField(default=1)
list_title = models.CharField(max_length=64)
desc = models.TextField(max_length=600)
img_url = models.URLField(max_length=200, null=True, blank=True)
start_bid = models.IntegerField()
category = models.CharField(choices=CATEGORIES, max_length=35, null=True, blank=True)
active = models.BooleanField(default=True)
def __str__(self):
return f"ID:{self.id}, {self.list_title}: {self.desc}, {self.start_bid} posted by user:{self.user_id} in Category:{self.category}, url:{self.img_url}"
class Bids(models.Model):
user_id = models.ForeignKey('User', on_delete=models.CASCADE)
auctions = models.ForeignKey('Auction_Listing', on_delete=models.CASCADE, default=1,related_name='bidauc')
bid = models.IntegerField()
def __str__(self):
return f"ID:{self.id}, Bid {self.bid} posted by user:{self.user_id} on auction {self.auctions}"
class Auction_Comments(models.Model):
user_id = models.ForeignKey('User', on_delete=models.CASCADE)
comment = models.TextField(max_length=324, default='N/A')
auctions = models.ForeignKey('Auction_Listing', on_delete=models.CASCADE, default=1,related_name='comauc')
def __str__(self):
return f"ID:{self.id}, Comment: {self.comment} posted by user:{self.user_id} on auction {self.auctions}"
class Watchlist(models.Model):
user_id = models.ForeignKey('User', on_delete=models.CASCADE)
auctions = models.ForeignKey('Auction_Listing', on_delete=models.CASCADE, default=1, related_name='watchauc')
def __str__(self):
return f"ID:{self.id}, user:{self.user_id} on auction {self.auctions}"
views.py
def render_listing(request, title):
if request.method == "POST":
form = BidForm(request.POST)
bid = int(request.POST['new_bid'])
listing = Auction_Listing.objects.get(list_title=title)
comments = Auction_Comments.objects.filter(auctions=listing)
if bid <= listing.start_bid:
error = True
else:
error = False
listing.start_bid = bid
listing.save()
new_bid = Bids(user_id=request.user, auctions=listing, bid=bid)
new_bid.save()
return render(request, 'auctions/listing.html', {
"listing": listing,
"form": form,
"comments": comments,
"error": error,
"comform": CommentForm()
})
else:
form = BidForm()
comform = CommentForm()
listing = Auction_Listing.objects.get(list_title=title)
comments = Auction_Comments.objects.filter(auctions=listing)
high_bid = Bids.objects.filter(auctions=listing).aggregate(maximum=Max("bid"))
winner = Bids.objects.filter(auctions=listing, bid=high_bid['maximum'])
print(winner)
return render(request, 'auctions/listing.html', {
"listing": listing,
"form": form,
"comments": comments,
"error": False,
"comform": comform,
"winner": winner
})
template's code:
{% extends "auctions/layout.html" %}
{% load static %}
{% block title %} Listing: {{listing.list_title}} {% endblock %}
{% block body %}
{% if listing.active %}
<h2>{{ listing.list_title }}</h2>
<div class='listing'>
{% if listing.img_url == "" or listing.img_url == None %}
<a href='#'><img src="{% static 'auctions/img404.png' %}" class='img-fluid'></a>
{% else %}
<a href='#'><img src="{{ listing.img_url }}" class="img-fluid" alt='image of {{ listing.list_title }}'></a>
{% endif %}
<p>
{{ listing.desc }}
</p>
<p>
Current Bid: ${{ listing.start_bid }}
</p>
<p>Category: {{ listing.category }}</p>
<p></p>
{% if user.is_authenticated %}
<div class="bid">
<a href='{% url "watch" listing.list_title %}' class='btn btn-primary'>Add to/Remove from Watchlist</a>
{% if listing.user_id == user.id %}
<a href='{% url "close" listing.list_title %}' class='btn btn-primary'>Close Auction</a>
{% endif %}
</div>
<div class="bid">
<h3>Bid:</h3>
<form method="POST" action='{% url "renlist" listing.list_title %}'>
{% csrf_token %}
{{form}}
<button type="submit" class='btn btn-primary'>Make New Bid</button>
{% if error %}
Please enter a bid higher than the current bid.
{% endif %}
</form>
</div>
{% else %}
<p><a href='{% url "register" %}' class='register'>Register</a> to bid on this item and gain access to other features</p>
{% endif %}
</div>
<div class="listing">
<h3>Comments:</h3>
{% if user.is_authenticated %}
<div id='comform'>
<h4>Post a Comment</h4>
<form method="POST" action="{% url 'commentadd' %}">
{% csrf_token %}
{{comform}}
<button type="submit" class="btn btn-primary">Post Comment</a>
</form>
</div>
{% endif %}
<p>
{% for comment in comments %}
<div class="comment">
<h4>{{comment.user_id.username}} posted:</h4>
<p>{{comment.comment}}</p>
</div>
{% empty %}
<h4>No comments as of yet</h4>
{% endfor %}
</p>
</div>
{% else %}
<h2>This auction has been closed</h2>
<div>
<a href='{% url "watch" listing.list_title %}' class='btn btn-primary'>Add to/Remove from Watchlist</a>
</div>
<p>{{winner.user_id.username}}</p>
{% endif %}
{% endblock %}
Thanks in advance for the help!
Kind Regards
PrimeBeat
This will give a QuerySet. You can see in your second image.
A QuerySet represents a collection of objects from your database.
winner = Bids.objects.filter(auctions=listing, bid=high_bid['maximum'])
You need to iterate over that QuerySet, get whatever data you want and show it in your template.
{% for win in winner %}
<p>{{ win.user_id.username }}</p>
{% endfor %}
I have a bootstrap search bar and I want it to search for a list of all tags in a database. Tags are a like a facebook page or reddit tags.
When I submit my search, I want the URL to include the GET-parameter: ?q="typeword", but the application has to redirect to a new page, and preserve the tags parameter while doing so. How can I do this?
I have tried using GET-request and paginator, but nothing seems to work. I can add a new URL "search/" and then it works, but only with this url.
models.py
class Tag(models.Model):
path = models.CharField(max_length=37,default=0)
name = models.CharField(max_length=35)
description = models.CharField(max_length=200)
image = models.ImageField(default='default.jpg',upload_to='tag_pics')
def __str__(self):
return self.path
views.py
def search(request):
q = request.GET.get('q',None)
items=''
if q is None or q is "":
items = Tag.objects.all()
elif q is not None:
items = Tag.objects.filter(name__contains=q)
paginator = Paginator(items, 3)
page = request.GET.get('page')
items = paginator.get_page(page)
title = "Search"
return render(request, 'blog/search.html',{'items': items, 'title': title})
search.html
{% extends 'blog/base.html' %}
{% block content %}
{% if items %}
<p class="search-count">Found
<strong>
{{ items.paginator.count }}
</strong>
result{{ items.paginator.count|pluralize }}
</p>
{% for i in items %}
{{ i.path }}
{% endfor %}
{% elif request.GET.q %}
<p class="search-no-results">
No results for "<strong>{{ request.GET.q }}"
</p>
{% endif %}
{% endblock %}
Wagtail Blog Site - Comment Model Form Issues
I'm creating a blog site, using Wagtail, and I've run in to a snag. I've added a Comment Model to my page, which I can add comments through the admin section and they display on the correct posts, but the comment form I've created is not displaying for some reason. Here's my relevant code. Any tips on where I went wrong would be greatly appreciated.
blog/models.py
class BlogPage(Page):
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
#tag manager
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
#get feature image
def main_image(self):
gallery_item = self.gallery_images.first()
if gallery_item:
return gallery_item.image
else:
return None
search_fields = Page.search_fields + [
index.SearchField('intro'),
index.SearchField('body'),
]
content_panels = Page.content_panels + [
MultiFieldPanel([
FieldPanel('date'),
FieldPanel('tags'),
], heading="Blog information"),
FieldPanel('intro'),
FieldPanel('body'),
InlinePanel('gallery_images', label="Gallery images"),
]
def serve(self, request):
# Get current page
post = self
# Get comment form
form = CommentForm(request.POST or None)
# Check for valid form
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect(request.path)
return render_to_response(self,
{
'post': post,
'form': form,
},
context_instance=RequestContext(request))
class Comment(models.Model):
post = models.ForeignKey(BlogPage, related_name='comments')
author = models.CharField(max_length=250)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __unicode__(self):
return self.text
def __str__(self):
return self.text
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)
blog_page.html
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %}
<div class="section">
<div class="container">
<h1 class="title">{{ page.title }}</h1>
<p class="meta subtitle">{{ page.date }}</p>
{% with page.main_image as main_image %}
{% if main_image %}{% image main_image fill-500x300 %}{% endif %}
{% endwith %}
<p>{{ main_image.caption }}</p>
<div class="hero-body subtitle">{{ page.intro }}</div>
<div class="content">
{{ page.body|richtext }}
{% if page.tags.all.count %}
<div class="tags">
<h3>Tags</h3>
{% for tag in page.tags.all %}
<span class="tag is-primary is-medium is-link"><a style="color: white" href="{% slugurl 'tags' %}?tag={{ tag }}">{{ tag }}</a></span>
{% endfor %}
</div>
{% endif %}
<p>Return to blog archive</p>
<hr>
<br>
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input class="control button is-primary" type='submit' name='submit' value='Add Comment'>
</form>
<br>
<hr>
<div class="section">
{% if page.comments.all.count %}
<h2 class='subtitle'>Comments</h2>
<div class="comments">
{% for comment in page.comments.all %}
{% if comment.approved_comment %}
<div class="comment">
<h5 class="date">{{ comment.created_date }}</h5>
<strong><h3 class="title is-3">{{ comment.author }}</h3></strong>
<h4 class="subtitle is-5">{{ comment.text|linebreaks }}</h4>
<br>
<hr>
</div>
{% endif %}
{% empty %}
<br>
<p>No comments yet...</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
Now I'm getting an error saying:
File "/home/kenneth/development/web/sites/mysite/dynamicsalesops/blog/models.py", line 88, in serve
context_instance=RequestContext(request))
TypeError: render_to_response() got an unexpected keyword argument 'context_instance'
Your view_post function is never used. In Wagtail, rendering pages as HTML is handled by a serve method on the page model itself, not by a separate view function: http://docs.wagtail.io/en/v1.9/reference/pages/theory.html#anatomy-of-a-wagtail-request
I am working on a django project and making a dashboard in which I am trying to add a search bar in which on typing I will get a list of all the relevant searches.
Views.py
def get_user_name(request):
if request.is_ajax():
results = []
context = RequestContext(request)
if request.method == 'GET':
q = request.GET.get('search_text')
results = User.objects.filter(user_name__contains = q)
context = {
'data': []
}
for record in results:
data = {
'id': record.id,
'code': record.code,
'name': record.name,
'manager': record.manager.email,
'type': record.type.upper(),
'status': record.current_state.display_name,
}
return render(request, 'listinguser.html', context)
listinguser.html
{% extends "base.html" %}
{% block title %}Users Listing Page{% endblock %}
{% block body %}
<div class='page_header'>
<div class="ui-widget">
<p align="right">
<input type="text" placeholder="Type user name" id="user_name" name="term">
</p>
</div>
<script>
$("#user_name").keypress(function(){
$.ajax({
type: "GET",
url: "{% url "user_name" %}",
data: {
'search_text' : $('#chain_name').val(),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
dataType: "json",
success: function(response) {
//success code
},
error: function(rs, e)
alert(rs.responseText);
}
});
})
</script>
</div>
<div class="col-md-6" style="width:100%">
<table id="data-table" class="table table-striped table-bordered">
<thead>
<tr>
<th style="width: 5%;">#</th>
<th style="width: 8%;">Id</th>
<th style="width: 15%;">Name</th>
<th style="width: 15%;">Status</th>
</tr>
</thead>
<tbody>
{% for record in data %}
<tr>
<td>{{ forloop.counter}}</td>
<td>{{ record.user_id }}</td>
<td>{{ record.user_name }} <br>({{ record.user_type }})</td>
<td>{{ record.user_status }}</td>
{% endfor %}
</tbody>
</table>
{% if page.has_other_pages %}
<ul class="pagination">
{% if page.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in page.paginator.page_range %}
{% if page.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if page.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
{% endblock %}
Problem I am facing:
While I type something in the search box the ajax func is called but it always goes into the error block.
Also I want the search result to get populated in the table just as the table normally is.
This is the API which I use to populate the table.
class UserListingView(View):
def get(self, request):
try:
page_size = int(request.GET.get('page_size', 10))
page_no = int(request.GET.get('page_no', 1))
except ValueError:
# Set the default value
page_size = 100
page_no = 1
filter_on = request.GET.get('current_state', None)
if 'listing_user' in request.user.permissions:
users = Users.objects.select_related(
"user", "user_state", "user_manager"
).all()
else:
users = User.objects.select_related(
"user", "user_state", "user_manager"
).filter(user_manager=request.user)
paginator = Paginator(integrations, page_size)
context = {
'page': {},
'data': []
}
try:
page = paginator.page(page_no)
context['current_page'] = page_no
except EmptyPage:
# Show the last page
page = paginator.page(paginator.num_pages)
context['current_page'] = paginator.num_pages
for record in page:
data = {
'user_id': record.id,
'user_code': record.user_code,
'user_id': record.user_id,
'user_name': record.user_name,
'user_manager': record.user_manager.email,
'user_type': record.user_type.upper(),
'user_status': record.user_state.display_name,
'user_status': user_status,
}
context['data'].append(data)
context['page'] = page
return render(request, 'listinguser.html', context)
I want to use django-import-export with class based views.
In the docs at https://django-import-export.readthedocs.org/en/latest/getting_started.html I see an example of exporting as csv
>>> dataset = BookResource().export()
>>> print dataset.csv
id,name,author,author_email,imported,published,price,categories
2,Some book,1,,0,2012-12-05,8.85,1
but which class based view should I use if I want to return an Excel file? Just View?
Jamgreen,
Based on implementation at https://github.com/bmihelac/django-import-export and considering a model "Country" as example with name and abreviation atributes:
First, define at the end of Country models file the Resource:
class CountryResource(resources.ModelResource):
class Meta:
model = Country
Then, implement the class based views:
class CountryExport(View):
def get(self, *args, **kwargs ):
dataset = CountryResource().export()
response = HttpResponse(dataset.csv, content_type="csv")
response['Content-Disposition'] = 'attachment; filename=filename.csv'
return response
class CountryImport(View):
model = Country
from_encoding = "utf-8"
#: import / export formats
DEFAULT_FORMATS = (
base_formats.CSV,
base_formats.XLS,
base_formats.TSV,
base_formats.ODS,
base_formats.JSON,
base_formats.YAML,
base_formats.HTML,
)
formats = DEFAULT_FORMATS
#: template for import view
import_template_name = 'Country/import.html'
resource_class = None
def get_import_formats(self):
"""
Returns available import formats.
"""
return [f for f in self.formats if f().can_import()]
def get_resource_class(self):
if not self.resource_class:
return modelresource_factory(self.model)
else:
return self.resource_class
def get_import_resource_class(self):
"""
Returns ResourceClass to use for import.
"""
return self.get_resource_class()
def get(self, *args, **kwargs ):
'''
Perform a dry_run of the import to make sure the import will not
result in errors. If there where no error, save the user
uploaded file to a local temp file that will be used by
'process_import' for the actual import.
'''
resource = self.get_import_resource_class()()
context = {}
import_formats = self.get_import_formats()
form = ImportForm(import_formats,
self.request.POST or None,
self.request.FILES or None)
if self.request.POST and form.is_valid():
input_format = import_formats[
int(form.cleaned_data['input_format'])
]()
import_file = form.cleaned_data['import_file']
# first always write the uploaded file to disk as it may be a
# memory file or else based on settings upload handlers
with tempfile.NamedTemporaryFile(delete=False) as uploaded_file:
for chunk in import_file.chunks():
uploaded_file.write(chunk)
# then read the file, using the proper format-specific mode
with open(uploaded_file.name,
input_format.get_read_mode()) as uploaded_import_file:
# warning, big files may exceed memory
data = uploaded_import_file.read()
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = resource.import_data(dataset, dry_run=True,
raise_errors=False)
context['result'] = result
if not result.has_errors():
context['confirm_form'] = ConfirmImportForm(initial={
'import_file_name': os.path.basename(uploaded_file.name),
'input_format': form.cleaned_data['input_format'],
})
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_fields()]
return TemplateResponse(self.request, [self.import_template_name], context)
def post(self, *args, **kwargs ):
'''
Perform a dry_run of the import to make sure the import will not
result in errors. If there where no error, save the user
uploaded file to a local temp file that will be used by
'process_import' for the actual import.
'''
resource = self.get_import_resource_class()()
context = {}
import_formats = self.get_import_formats()
form = ImportForm(import_formats,
self.request.POST or None,
self.request.FILES or None)
if self.request.POST and form.is_valid():
input_format = import_formats[
int(form.cleaned_data['input_format'])
]()
import_file = form.cleaned_data['import_file']
# first always write the uploaded file to disk as it may be a
# memory file or else based on settings upload handlers
with tempfile.NamedTemporaryFile(delete=False) as uploaded_file:
for chunk in import_file.chunks():
uploaded_file.write(chunk)
# then read the file, using the proper format-specific mode
with open(uploaded_file.name,
input_format.get_read_mode()) as uploaded_import_file:
# warning, big files may exceed memory
data = uploaded_import_file.read()
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = resource.import_data(dataset, dry_run=True,
raise_errors=False)
context['result'] = result
if not result.has_errors():
context['confirm_form'] = ConfirmImportForm(initial={
'import_file_name': os.path.basename(uploaded_file.name),
'input_format': form.cleaned_data['input_format'],
})
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_fields()]
return TemplateResponse(self.request, [self.import_template_name], context)
class CountryProcessImport(View):
model = Country
from_encoding = "utf-8"
#: import / export formats
DEFAULT_FORMATS = (
base_formats.CSV,
base_formats.XLS,
base_formats.TSV,
base_formats.ODS,
base_formats.JSON,
base_formats.YAML,
base_formats.HTML,
)
formats = DEFAULT_FORMATS
#: template for import view
import_template_name = 'Country/import.html'
resource_class = None
def get_import_formats(self):
"""
Returns available import formats.
"""
return [f for f in self.formats if f().can_import()]
def get_resource_class(self):
if not self.resource_class:
return modelresource_factory(self.model)
else:
return self.resource_class
def get_import_resource_class(self):
"""
Returns ResourceClass to use for import.
"""
return self.get_resource_class()
def post(self, *args, **kwargs ):
'''
Perform the actual import action (after the user has confirmed he
wishes to import)
'''
opts = self.model._meta
resource = self.get_import_resource_class()()
confirm_form = ConfirmImportForm(self.request.POST)
if confirm_form.is_valid():
import_formats = self.get_import_formats()
input_format = import_formats[
int(confirm_form.cleaned_data['input_format'])
]()
import_file_name = os.path.join(
tempfile.gettempdir(),
confirm_form.cleaned_data['import_file_name']
)
import_file = open(import_file_name, input_format.get_read_mode())
data = import_file.read()
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = resource.import_data(dataset, dry_run=False,
raise_errors=True)
# Add imported objects to LogEntry
ADDITION = 1
CHANGE = 2
DELETION = 3
logentry_map = {
RowResult.IMPORT_TYPE_NEW: ADDITION,
RowResult.IMPORT_TYPE_UPDATE: CHANGE,
RowResult.IMPORT_TYPE_DELETE: DELETION,
}
content_type_id=ContentType.objects.get_for_model(self.model).pk
'''
for row in result:
LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=content_type_id,
object_id=row.object_id,
object_repr=row.object_repr,
action_flag=logentry_map[row.import_type],
change_message="%s through import_export" % row.import_type,
)
'''
success_message = _('Import finished')
messages.success(self.request, success_message)
import_file.close()
url = reverse('%s_list' % (str(opts.app_label).lower()))
return HttpResponseRedirect(url)
the template import.html has the following code:
<h1>{% trans "Importar" %} {{ opts.app_label }}</h1>
{% if confirm_form %}
<form action="{% url "process_import" %}" method="POST">
{% csrf_token %}
{{ confirm_form.as_p }}
<p>
{% trans "Below is a preview of data to be imported. If you are satisfied with the results, click 'Confirm import'" %}
</p>
<div class="submit-row">
<input type="submit" class="btn" name="confirm" value="{% trans "Confirm import" %}">
</div>
</form>
{% else %}
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% trans "This importer will import the following fields: " %}
{% for f in fields %}
{% if forloop.counter0 %}
,
{% endif %}
<tt>{{ f }}</tt>
{% endfor %}
</p>
<fieldset class="module aligned">
{% for field in form %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
{% if field.field.help_text %}
<p class="help">{{ field.field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="submit" class="btn" value="{% trans "Submit" %}">
</div>
</form>
{% endif %}
{% if result %}
{% if result.has_errors %}
<h2>{% trans "Errors" %}</h2>
<ul>
{% for error in result.base_errors %}
<li>{{ error.error }}</li>
{% endfor %}
{% for line, errors in result.row_errors %}
{% for error in errors %}
<li>
{% trans "Line number" %}: {{ line }} - {{ error.error }}
<div class="traceback">{{ error.traceback|linebreaks }}</div>
</li>
{% endfor %}
{% endfor %}
</ul>
{% else %}
<h2>
{% trans "Preview" %}
</h2>
<table>
<thead>
<tr>
<th></th>
{% for field in fields %}
<th>{{ field }}</th>
{% endfor %}
</tr>
</thead>
{% for row in result.rows %}
<tr>
<td>
{% if row.import_type == 'new' %}
{% trans "New" %}
{% elif row.import_type == 'skip' %}
{% trans "Skipped" %}
{% elif row.import_type == 'delete' %}
{% trans "Delete" %}
{% elif row.import_type == 'update' %}
{% trans "Update" %}
{% endif %}
</td>
{% for field in row.diff %}
<td>
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
{% endif %}
and the urls.py should contain:
#export
url(r'export/$', login_required(CountryExport.as_view()), name='country_export'),
#import
url(r'import/$', login_required(CountryImport.as_view()), name='country_import'),
url(r'process_import/$', login_required(CountryProcessImport.as_view()), name='process_import'),
I've done the exact same thing today. I tried to make the view fairly generic and allow values to be passed through in the URL so it is a little more complex than simply returning an XLS file.
But anyway, this was my approach;
class PlaceResource(resources.ModelResource):
class Meta:
model = Place
class ExportPlacesView(View):
model = Place
def get(self, request, *args, **kwargs):
client_name = str(self.client).replace(' ', '_').lower()
if 'query' in kwargs:
if kwargs['query'] == u'complete':
complete_runners = Place.objects.filter(
~models.Q(runner__completed_on=None),
console=self.console).values_list(
'client',
flat=True).distinct()
dataset = PlaceResource().export(
Place.objects.filter(
console=self.console, client=self.client,
runner__in=complete_runners
)
)
filename = '{}_completed_runners'.format(client_name)
else:
dataset = PlaceResource().export(Place.objects.filter(
console=self.console, client=self.client,
))
filename = '{}_Runners'.format(client_name)
export_type = kwargs['format']
_dataset_methods = {
'csv': dataset.csv,
'xls': dataset.xls
}
response = HttpResponse(
_dataset_methods[export_type], content_type=export_type
)
response[
'Content-Disposition'] = 'attachment; filename={filename}.{ext}'.format(
filename=filename,
ext=export_type
)
return response