Django view variables based on url - django

I'm just starting off with python so bear with me.
Lets say this is my model:
class Event(models.Model):
name = models.CharField(max_length=100, verbose_name=_('name'))
location = models.CharField(max_length=200, verbose_name=_('location'))
start_time = models.DateTimeField(verbose_name=_('start time'))
end_time = models.DateTimeField(verbose_name=_('end time'))
sales_start = models.DateField(verbose_name=_('sales start'))
sales_end = models.DateField(verbose_name=_('sales end'))
event_active = models.BooleanField(verbose_name=_('event active'))
price = models.IntegerField(verbose_name=_('price'))
maximum = models.IntegerField(verbose_name=_('maximum'))
information = models.CharField(max_length=500, verbose_name=_('information'))
logo = models.ImageField(verbose_name=_('logo'), blank=True, upload_to='img/%Y/%m/%d')
What I would like to accomplish is make a single page and fill it with data from an 'event', I would like to get this event by getting the Id from the url.
I'm not sure on how to do this as I don't understand certain parts of the documentation as I'm not a native English speaker.

I think this is what you are trying to do:
# urls.py
# note 'event_id' will be passed as argument to 'show_event' view.
url(r'events/(?P<event_id>\d+)/$', 'show_event', name='show_event')
# views.py
def show_event(request, event_id):
...
# this will return a 404 response is case event with given id is not found
event = get_object_or_404(Event, id=event_id)
...
return render(request, 'template.html', {'event': event})
# template.html
<h1>Welcome to the event {{event.name}}</h1>
You will use URLs in this way: yourdomain.com/events/123.
That would pull 123 event id from URL and render template.html sending the proper event object in the template context, so you can render it as you want.

So your urls.py will need to be designed to except a regular expression of anything your page name could be. In the example provided by Martin you can also have a regular expression match text instead of just a number.

Related

Django asyncio for saving (big amount of) objects - Nothing saved

I want to fetch categories from a Magento API and display them in a template. In the same time, I want to save them in DB for an ulterior use.
Categories are too many and the render of the template takes more than 30 sec.
I start to learn using asyncio but couldn't get my way with it. I surely missed something.
First, my URL leads to the function that retrieves the categories
#login_required
def get_categories(request):
Category.objects.all().delete()
try:
cats = fetch_categories()
tree = cats['children_data']
except:
print('erreur : impossible de récupérer les catégories (fetch_categories)')
asyncio.run(parse_categories(tree))
return render(request, 'categories/categories_list.html', {'tree': tree})
When I get the "categories tree", I send it to
async def parse_categories(tree):
for lvl1 in tree:
all_tasks = []
asyncio.create_task(save_cat(lvl1))
# main products categories (turbo, injectors ...)
for lvl2 in lvl1['children_data']:
asyncio.create_task(save_cat(lvl2))
# sub categories like RENAULT, DACIA
for lvl3 in lvl2['children_data']:
asyncio.create_task(save_cat(lvl3))
for lvl4 in lvl3['children_data']:
asyncio.create_task(save_cat(lvl4))
for lvl5 in lvl4['children_data']:
asyncio.create_task(save_cat(lvl5))
My save() function is async. I'm not sure it should be. Before I started using async, it was working.
async def save_cat(cat):
cat_id = cat['id']
new_cat = Category()
new_cat.id = cat_id
new_cat.name = cat.get('name', None)
new_cat.parent = cat.get('parent_id', None)
new_cat.url = cat.get('path', None)
new_cat.is_active = cat.get('is_active', None)
new_cat.position = cat.get('position', None)
new_cat.level = cat.get('level', None)
new_cat.save()
When I run, no error. The context is well sent to the template and displays well. But no category is saved.
I also tried to make a task list with asyncio.create_task in each level and execute the loop at the end of parse_categories() like said in this thread, without success.
all_tasks.append(asyncio.create_task(save_cat(lvl1)))
[...]
responses = asyncio.gather(*all_tasks, return_exceptions=True)
loop = asyncio.get_event_loop()
loop.run_until_complete(responses)
loop.close()
Any clue to solve my case will be welcome

Error "No Models matches the given query" after using get_next_by_FOO django

When using get_next_by_Foo, it doesnt show content in template file.
I get data from this query:
request.session['contract_posts_search'] is results I get from filtering in another view.
def contract_detail_test(request, contract_id=None):
context = {}
contract_posts_search=request.session['contract_posts_search']
contract_filtered=[]
for i in range(len(contract_posts_search)):
contract_filtered.append(contract_posts_search[i]["fields"]["contract"])
contract_posts_search= Contracts.objects.filter(contract__in=contract_filtered).distinct()
contract_posts = get_object_or_404(contract_posts_search, contract=contract_id)
After having contract_post, I use get_next_by_created_at:
if contract_posts is not None:
print(contract_posts)
try:
the_next = contract_posts.get_next_by_created_at()
except:
the_next=None
try:
the_prev = contract_posts.get_previous_by_created_at()
except:
the_prev=None
context = { "contract_posts": contract_posts,
"the_next" : the_next,
"the_prev": the_prev,
}
return render(request, "contract_detail_test.html", context)
My ulr:
path('contract_detail_test/<str:contract_id>/', contract_detail_test, name="contract_detail_test")
My model:
class Contracts(models.Model):
id=models.AutoField(primary_key=True)
contract=models.CharField(max_length=255,blank=True, null=True)
name=models.CharField(max_length=255,blank=True, null=True)
debt=models.IntegerField(blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True,blank=True)
updated_at=models.DateTimeField(auto_now_add=True,blank=True)
objects=models.Manager()
class Meta:
ordering=["-created_at","-id"]
def get_absolute_url(self):
return "/contract_detail_test/%s/" % self.contract
For example:
I filter contract contain "2016" and get 10 results, using request_session in another view, I can print out 10 results.
Then I use get_next_by_created_at, It will show first result correctly, but after clicking, it will show
this error. There is no contract number 20170709-0010161 in contract_posts
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/contract_detail_test/20170709-0010161/
Raised by: test_upload_filter.views.contract_detail_test
No Contracts matches the given query.

Django Admin: how to display a url as a link while calling specific function to download the file

Title is a bit confusing, but basically I have an s3 path stored as a string
class S3Stuff(Model):
s3_path = CharField(max_length=255, blank=True, null=True)
# rest is not important
There are existing methods to download the content given the url, so I want to utilize that
def download_from_s3(bucket, file_name):
s3_client = boto3.client(bleh_bleh)
s3_response = s3_client.get_object(Bucket=s3_bucket, Key=file_name)
return {'response': 200, 'body': s3_response['Body'].read()}
s3_path can be broken into bucket and file_name. This works very easily when I use my own frontend because I can do whatever I want with it, but I don't know how to apply this to admin
class S3StuffAdmin(admin.StackedInline):
model = S3Stuff
fields = ('s3_path', )
Now how do I call that method and make the display a link that says "download"
I don't think this function will be much useful for generating download links, instead use the boto3's presigned_url like this:
from django.utils.html import format_html
class S3StuffAdmin(admin.StackedInline):
model = S3Stuff
fields = ('s3_path', )
readonly_field = ('download',)
def download(self, obj):
s3_client = boto3.client(bleh_bleh)
url = s3_client.generate_presigned_url('get_object', Params = {'Bucket': 'bucket', 'Key': obj.s3_path}, ExpiresIn = 100)
return format_html('<a href={}>download</a>'.format(url))

using two templates from one view

I am trying to present content from a view in two ways: html and csv download. The only way I was able to do it was to use 2 different views, one for html presentation and one for csv. This duplicates my code and I am looking for a more elegant solution.
Any suggestions?
Here is the sample code:
# views.py
[...]
def member_list(request):
member_list = Member.objects.all()
return render_to_response("member_list.html",
{'member_list':member_list)
def member_csv_list(request):
member_list = Member.objects.all()
csv_list = HttpResponse(content_type='text/csv')
csv_list['Content-Disposition'] = 'attachment; filename="member_list.csv"'
writer = csv.writer(csv_list)
writer.writerow(['Name', 'Member Type', 'Rooms'])
for member in member_list:
fields = [member.name, member.member_type, member.room]
writer.writerow(fields)
return member_list
You can use a parameter in your url and implement a view like
def myview(request) :
type = request.GET.get('type', 'html')
# do processing
if type == 'html':
# return html
else if type == 'csv':
# return csv
If you access a url like http://yourserver/myview?type=csv it will render the csv part of the view. When the url http://yourserver/myview is accessed it will return the html part of the view.
Rohan's answer is absolutely the right paradigm. For an excellent tutorial-style introduction to this topic, cf. Multiple Templates in Django.
Here are a few quotes (all credit goes to Scott Newman).
To serve a printable version of an article, for example, we can add ?printable to the end of the URL.
To make it work, we'll add an extra step in our view to check the URL for this variable. If it exists, we'll load up a printer-friendly template file. If it doesn't exist, we'll load the normal template file.
def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)
if request.GET.has_key('printable'):
template_file = 'press/detail_printable.html'
else:
template_file = 'press/detail.html'
t = loader.get_template(template_file)
c = Context({'press': p})
return HttpResponse(t.render(c))
He continues with template overrides and different templates by domain names. All this is excellent.

Correct Django URL config to read JSON

I'm trying to learn a bit of REST. I have added several views to an existing Django app to try and do various things using REST and JSON. I am able to get my app to send out requested data through several views, but I can't seem to get it to accept JSON as part of the URL.
I have created a view that looks like this:
def restCreateEvent(request, key, jsonString):
errors = checkKey(key)
if errors == None:
eventJson = json.loads(jsonString)
eventInfo = eventJson['event']
title = eventInfo['title']
description = eventInfo['description']
locationInfo = eventInfo['location']
place = locationInfo['place_name']
street = locationInfo['street_address']
city = locationInfo['city']
state = locationInfo['state']
zip = locationInfo['zip']
event = models.Event()
event.title = title
event.description = description
event.street_address = street
event.place_name = place
event.city = city
event.state = state
event.zip = zip
event.save()
else:
return errors
However, I can;t seem to get the URL correct, here is what I have now:
(r'^events/rest/create/(?P<key>\d+)/(?P<jsonString>.+)', 'events.views.restCreateEvent')
When I attempt to access the following url, Django debug complains that none of my urls match it.
http://127.0.0.1:8000/events/rest/33456/create/{"title":"test","description":"this is a test","location":{"place_name":"somewhere","street_address":"123 main","city":"pittsburgh","state":"pa","zip":"11111"}}
Right now the view is never called, so obviously my url is wrong. So, is my approach totally wrong here? If not how do I fix the url?
Why would you do this? The way to send JSON, like any payload, is to put it in the POST data, not the URL.