django: download filtered data as csv - django

Hej!
I want to download my data as a csv file.
My problem is that I can't get my filters applyed in the downloaded csv, it'll always give me the whole list.
I tried to connect the two functions but it doesn't work as expected. But they both run fine on their own.
Does anyone know how to achieve that? Or knows what I'm doing wrong?
Any help is appreciated! :)
# views.py
def download_csv(request):
institutions = Institution.objects.all()
filter = InstitutionFilter(request.GET, queryset=institutions).qs
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="institutions.csv"'
writer = csv.writer(response)
writer.writerow(['Name', "Abbreviation", "Parent Institution", "Phone Number"])
for institution in filter.values_list('name', 'abbreviation', 'parent_institution__name', 'contact_details'):
writer.writerow(institution)
return response
# filters.py
class InstitutionFilter(django_filters.FilterSet):
name = CharFilter(field_name="name", lookup_expr="icontains")
class Meta:
model = Institution
fields = "__all__"

I had to add the filter parameter to the url to get them working. So the solution lies in the template rather then in the views :)
<div class="col">
Download CSV
</div>
EDIT:
#csv_download/urls.py
app_name = "csv_download"
urlpatterns = [
path("export-institution/", download_csv_institution, name="download_institutions"),
]
# general urls.py
urlpatterns = [
path("admin/", admin.site.urls, name="admin"), # admin site
path("download_csv/", include("csv_download.urls")), # download app
]

Related

Cloudinary shown incorrect url in Django

I have created a model that uploads img to cloudinary, however, shows a wrong URL in the Django template which has 2 'https://' parts inside of it. Please help.
models.py:
class Product(models.Model):
img = models.ImageField(upload_to='product', height_field=None, width_field=None, max_length=100,
default='https://res.cloudinary.com/dgdhnbsae/image/upload/vxxxx/product/xxxx.jpg')
def get_img(self):
return f"{'/media/product/'+self.img}"
I have set the related configuration according to the tutorial
setting.py:
INSTALLED_APPS = [
xxxx
'cloudinary_storage',
'cloudinary',
]
CLOUDINARY_STORAGE = {
'CLOUD_NAME': 'xxxx',
'API_KEY': 'xxxxx',
'API_SECRET': 'xxxxx'
}
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
Template:
<div class="featured__item__pic set-bg" data-setbg="{{ product.img.url }}">
The output:
https://res.cloudinary.com/xxxx/image/upload/v1/media/https://res.cloudinary.com/vxxxx/image/upload/xxxxx/xxxxx.jpg
So if I understood your problem correctly,
youll need to do the below changes
in your model.py file, add below
from cloudinary.models import CloudinaryField
instead of using the built in function for the image upload, you'd want to use the cloudinary function.
eg:
img = CloudinaryField('attributes')
you can later call this in views as usual and then pass it on to your django template. or you can use cloudinary template call, which will allow you to parse additional image manipulation options into the url string.
Some references:
https://jszczerbinski.medium.com/django-web-app-and-images-cloudinary-straightforward-study-ae8b5bb03e37
an easier reference:
https://alphacoder.xyz/image-upload-with-django-and-cloudinary/

Django url path converter not working in production

I'm using path converter in my django app like so:
# urls.py
from . import views
from django.urls import path
urlpatterns = [
path('articles/<str:collection>', views.ArticleView),
]
# views.py
#login_required
def ArticleView(request, collection):
print(collection)
if collection == "None":
articles_query = ArticleModel.objects.all()
...
This works fine in development for a url suck as : http://localhost:8000/articles/My Collection which gets encoded to http://localhost:8000/articles/My%20Collection, and is decoded properly in the ArticleView. However, in development, I have to edit the view like so to get it to work:
# views.py
import urllib.parse
#login_required
def ArticleView(request, collection):
collection = urllib.parse.unquote(collection)
print(collection)
if collection == "None":
articles_query = ArticleModel.objects.all()
...
Otherwise, the print(collection) shows My%20Collection and the whole logic in the rest of the view fails.
requirements.txt
asgiref==3.2.10
Django==3.1.1
django-crispy-forms==1.9.2
django-floppyforms==1.9.0
django-widget-tweaks==1.4.8
lxml==4.5.2
Pillow==7.2.0
python-pptx==0.6.18
pytz==2020.1
sqlparse==0.3.1
XlsxWriter==1.3.3
pymysql
What am I doing wrong here?
Thanks in advance!
The URL is being urlencoded which encodes spaces as %20. There are a number of other encodings. As you've discovered you need to decode that parameter in order to compare it to what you'd expect. As you've likely realized, if you have a value that actually wants The%20News and not The News, you have no recourse. To handle this people will create a slug field. Django has a model field for this in the framework.
This is typically a URL-friendly, unique value for the record.
Assuming you add a slug = models.SlugField() to ArticleModel, your urls and view can change into:
urlpatterns = [
# Define a path without a slug to identify the show all code path and avoid the None sentinel value.
path('articles', views.ArticleView, name='article-list'),
path('articles/<slug:slug>' views.ArticleView, name='article-slug-list'),
]
#login_required
def ArticleView(request, slug=None):
articles_query = ArticleModel.objects.all()
if slug:
articles_query = articles_query.filter(slug=slug)

Testing a query parameter in django restframework

I am testing a query parameter in djnago restframework to retrieve the detail of an object. The query works in the browser but not the test. I think I am not calling the response correctly with:
response = self.client.get(reverse('quote-requests:get-calculator-detail', kwargs={'calculator_code': self.calc1.calculator_code}))
the apps urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('api/v1/quote-requests/', include('quote_requests.urls')),
]
which includes the quotes.urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from quote_requests import views
router = DefaultRouter()
router.register('get-calculator', views.CalculatorCodeDetailViewSet, basename='get-calculator')
app_name = 'quote-requests'
urlpatterns = [
path('', include(router.urls)),
]
The viewset is:
class CalculatorCodeDetailViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = CalculatorCodeSerializer
lookup_field = ('calculator_code')
def get_queryset(self):
return (
Calculator.objects.filter(
calculator_code = self.request.query_params.get('calculator_code',)
)
)
The CalculatorCodeSerializer is:
class CalculatorCodeSerializer(serializers.ModelSerializer):
class Meta:
model = Calculator
fields = (
'name',
'calculator_code',
)
The test is:
def test_retrieving_calculator_detail_with_calculator_code(self):
''' test retrieving detail of a calculator '''
self.calc1 = Calculator.objects.create(
name = "Calculator 1000",
calculator_code = "HH1000",
)
response = self.client.get(reverse('quote-requests:get-calculator-detail', kwargs={'calculator_code': self.calc1.calculator_code}))
serializer = CalculatorCodeSerializer(self.calc1)
self.assertEqual(response.data, serializer.data)
This yields the error:
Traceback (most recent call last):
File "/app/quote_requests/tests/test_calculators_api.py", line 149, in test_retrieving_calculator_detail_with_calculator_code
self.assertEqual(response.data, serializer.data)
AssertionError: {'detail': ErrorDetail(string='Not found.',[14 chars]nd')} != {'name': 'Calculator 1000', 'calculator_cod[368 chars].25'}
When checking the browser link:
http://localhost:8000/api/v1/quote-requests/get-calculator/?calculator_code=HH1000
This works but test fails. Any help setting up the properly would be appreciated.
I think you are using the wrong endpoint name replace this "get-calculator-detail" with this "get-calculator"
def test_retrieving_calculator_detail_with_calculator_code(self):
# test retrieving detail of a calculator
self.calc1 = Calculator.objects.create(
name = "Calculator 1000",
calculator_code = "HH1000",
)
response = self.client.get(reverse('quote-requests:get-
calculator', kwargs={'calculator_code':
self.calc1.calculator_code}))
serializer = CalculatorCodeSerializer(self.calc1)
self.assertEqual(response.data, serializer.data)
With the help of others on Fiverr I have gotten the test to pass. I hesitate to call it answered but I am putting the detail here in case those who know a better way can comment.
TLDR - the response variable was not written correctly either the reverse does not do what I want or it cannot be done with reverse. Either way the change that worked was:
response = self.client.get('/api/v1/quote-requests/get-calculator/', {'calculator_code': self.calc1.calculator_code})
This does not return an iterable object. It returns an OrderDict which looks like this:
[{"name":"Calculator 1000","calculator_code":"HH1000","id":7,}]
I used print(response.content) to check this.
so you need to use the json function to pull the data out and match it to serializer.data - the response from the serializer.
response.data = response.json()[0]
this pulls the "[]" off and formats it like this:
{'name': 'Calculator 1000', 'calculator_code': 'HH1000', 'id': 7, }
The [0] on the end gets the object (I think) and returns it in json format.
The two fiverr users that helped me with this were:
https://www.fiverr.com/tailongk
and
https://www.fiverr.com/asipita

Django - Bad request syntax or unsupported method

I want to post data via an api onto my django app. This is how far I got:
import pandas
import requests
excel_data_df = pandas.read_excel('workorders.xlsx')
json_str = excel_data_df.to_json(orient='records', date_format='iso')
API_ENDPOINT = "http://127.0.0.1:8000/api/create/"
API_KEY = "dF8NbXRA.94Mj2xeXT3NZOtx1b575CvNvbs8JWo0D"
data = {'api_dev_key':API_KEY,
'api_option':'paste',
'api_paste_code':json_str ,
'api_paste_format':'csv'}
r = requests.post(url = API_ENDPOINT, data = data)
Django
views.py
class PostDataView(CreateAPIView):
queryset = Workorder.objects.all()
serializer_class = WorkorderSerializer
serializers.py
class WorkorderSerializer(serializers.ModelSerializer):
class Meta:
model = Workorder
exclude = ['id']
urls.py
from django.conf.urls import url, include
from .views import *
from rest_framework.urlpatterns import format_suffix_patterns
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
url(r'^api/chart/data/$', ChartData.as_view(),name="api-data"),
url(r'^api/create/$', PostDataView.as_view(), name='create'),
url(r'^(?P<pk>\d+)/api/delete/$', DeleteDataView.as_view(), name='delete'),
url(r'^(?P<pk>\d+)/api/update/$', UpdateDataView.as_view(), name='update'),
url(r'^$', display_mobiles, name="display_mobiles"),
url(r'^(?P<pk>\d+)$', edit_mobile, name="edit_mobile"),
url(r'^delete/(?P<pk>\d+)$', delete_mobile, name="delete_mobile"),
url(r'^home/$', index, name='index'),
url(r'^sitemap/$', sitemap, name='sitemap'),
url(r'^upload/$', upload, name='upload'),
url(r'^test/$', testview, name='test')
]
if settings.DEBUG:
urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns = format_suffix_patterns(urlpatterns)
models.py
class Workorder(models.Model):
label = models.CharField(max_length=200, blank=False)
start = models.DateField()
end = models.DateField()
duration = models.IntegerField()
ctype = models.CharField(max_length=20, default='bar')
werk = models.CharField(max_length=50, default='plant')
product = models.CharField(max_length=50)
train_number = models.IntegerField()
latest_start_date = models.DateField()
latest_start_timebucket = models.IntegerField()
is_start_date_fixed = models.BooleanField()
assigned_start_timebucket = models.IntegerField()
assigned_start_date = models.DateField()
costs_early_start = models.IntegerField()
costs_late_start = models.IntegerField()
resource_overall_demands = models.IntegerField()
resource_timeslots_demands = models.IntegerField()
I can enter the data manually using the post forms # api/create but when I try to post it via the api, I get this error:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 400</p>
<p>Message: Bad Request.</p>
<p>Error code explanation: 400 - Bad request syntax or unsupported method.</p>
</body>
</html>
This is the data I tried to post:
1)
[
{"id":null,"label":"Workorder 1","start":"2019-01-01T00:00:00.000Z","end":"2019-01-10T00:00:00.000Z","duration":9,"ctype":"bar","werk":"a","product":"a","train_number":535435.0,"latest_start_date":"2019-01-10T00:00:00.000Z","latest_start_timebucket":9,"is_start_date_fixed":false,"assigned_start_timebucket":535435.0,"assigned_start_date":"2019-01-10T00:00:00.000Z","costs_early_start":334,"costs_late_start":334,"resource_overall_demands":334,"resource_timeslots_demands":334},
{"id":null,"label":"Workorder 1","start":"2019-01-01T00:00:00.000Z","end":"2019-01-10T00:00:00.000Z","duration":9,"ctype":"bar","werk":"a","product":"a","train_number":535435.0,"latest_start_date":"2019-01-10T00:00:00.000Z","latest_start_timebucket":9,"is_start_date_fixed":false,"assigned_start_timebucket":535435.0,"assigned_start_date":"2019-01-10T00:00:00.000Z","costs_early_start":334,"costs_late_start":334,"resource_overall_demands":334,"resource_timeslots_demands":334}
]
2)
[
{"id":null,"label":"Workorder 1","start":"2019-01-01T00:00:00.000Z","end":"2019-01-10T00:00:00.000Z","duration":9,"ctype":"bar","werk":"a","product":"a","train_number":535435,"latest_start_date":"2019-01-10T00:00:00.000Z","latest_start_timebucket":9,"is_start_date_fixed":false,"assigned_start_timebucket":535435,"assigned_start_date":"2019-01-10T00:00:00.000Z","costs_early_start":334,"costs_late_start":334,"resource_overall_demands":334,"resource_timeslots_demands":334}
]
3)
{"id":null,"label":"Workorder 1","start":"2019-01-01T00:00:00.000Z","end":"2019-01-10T00:00:00.000Z","duration":9,"ctype":"bar","werk":"a","product":"a","train_number":535435,"latest_start_date":"2019-01-10T00:00:00.000Z","latest_start_timebucket":9,"is_start_date_fixed":false,"assigned_start_timebucket":535435,"assigned_start_date":"2019-01-10T00:00:00.000Z","costs_early_start":334,"costs_late_start":334,"resource_overall_demands":334,"resource_timeslots_demands":334}
4)
{"id":1}
Thank you for any help
When you do requests.post(url = API_ENDPOINT, data = data) it sends yoru data as form data.
It looks like you are trying to send data in a format which your API doesn't accept.
I think your API is configured to accept only JSON but you are trying to send it as form data.
Check settings from the docs at https://www.django-rest-framework.org/api-guide/parsers/#setting-the-parsers
Also you can try to post your data as JSON strings using requests.post(..., json=payload). Reference https://requests.readthedocs.io/en/master/user/quickstart/#more-complicated-post-requests
I don't understand what the api_... are referring to.
But given the error message, it seems that your client is not sending a proper request.
One usefull way of debugging this is to try to make it work first with generated code from the devtools:
Go to the browsable API from DRF
Open devtools, go to Network, select All
Make your POST via the browsable API
Find the corresponding request in the devtools, right click on it and select copy as fetch or copy as curl
Then try to run it on a browser (for fetch) or in bash (for curl) and see if it works.

Django REST framework RetrieveAPIView gets empty "id" parameter and returns 404 error

I use Django 1.11 + Django REST Framework. I'm trying to get detailed data about distinct record using generic RetrieveAPIView from Django REST Framework and server returns "HTTP 404 Not Found" result, i.e. no data.
models.py file:
class TestPage( models.Model):
title = models.CharField( size = 120)
counter = models.IntegerField( default = 0)
def __unicode__(self):
return u'' + self.title
serializers.py file:
class TestPageSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'counter',)
model = models.TestPage
urls.py file:
urlpatterns = [
url( r'^admin/', admin.site.urls),
url( r'^api/', include( 'rest_framework.urls')),
url( r'^api/', include( 'testpages.urls')),
]
testpages/urls.py file:
urlpatterns = [
url( r'^$', views.TestPageList.as_view()),
url( r'^(?P<id>)\d+/$', views.TestPageDetail.as_view()),
]
and at last views.py file:
class TestPageList(generics.ListAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
class TestPageDetail(generics.RetrieveAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
# def get_object( self):
# print self.kwargs
If I request "http://127.0.0.1:8000/api/" for all records in the list, server returns all records. But if I want to get record by id using "http://127.0.0.1:8000/api/1/" for example, I get the empty result like in the photo below.
[![enter image description here][1]][1]
If I uncomment get_object()-function in last 2 lines, I can see {'id': u''} in terminal i.e. server gets empty parameter 'id'.
What wrong with this code?
The issue is your regular expression matching for id, you're putting \d+ outside of the matching group when it's actually what you want to match:
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view())
By the way, to be good REST citizen, you shouldn't add the / at the end of a URL for a specific object.
Note: As mentioned by JPG, adding a name for the resource you're fetching (plural) would be proper RESTful:
url(r'^pages/(?P<id>\d+)$', views.TestPageDetail.as_view())
This way, you fetch page nr. 1 at /api/pages/1
The original problem was the closing parathesis (the regex expression), you were misplaced that.
urlpatterns = [
url(r'^$', views.TestPageList.as_view()),
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view()),
^^^ here
]
Apart from that, You should change the urls patterns to a sensible form, as
#testpages/urls.py
urlpatterns = [
url(r'^test/$', views.TestPageList.as_view()),
url(r'^test/(?P<id>\d+)/$', views.TestPageDetail.as_view()),
]
then the list-api will be available on /api/test/ endpoint and the detail-api will be available on /api/test/{id}/