I have activated i18n in my Django project and used i18n_patterns in my urls.py. All translations work fine and change whenever the language code in the URL changes. The problem comes to the picture in the media folder.
I have a few pictures in the media folder as the slider background and they get changed every few seconds. With this i18n_patterns, it reads media files from /en/media/ instead of /media/. In this case, I created the /en/media/ folder just for English and created the same folder /zh/media/ folder for Chinese. But the problem is, it always returns 404 even the images are there. All images in the /zh/media/ folder do not show. And only 2 out of 4 images in the /en/media/ folder shows. This is very confusing to me. Hope you guys have any idea of what is happening here.
Below is the portion of code in related files:
settings.py
LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE_PATHS = [
os.path.join(BASE_DIR, 'locale')
]
LANGUAGES = [
('en', _('English')),
('zh', _('Chinese')),
]
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
urls.py
urlpatterns = [
]
urlpatterns += i18n_patterns(
path('admin/', admin.site.urls),
path('', include('pages.urls')),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
template.html
<img src="media/{% image_file_name %}" />
On the console, it shows as follows even though the file is present.
[TIMESTAMP] "GET /zh/media/XXX.jpg HTTP/1.1" 404 2798
It is a bad practice to use <img src="media/{% image_file_name %}" />.
Instead do this <img src="{{ image_field.url }}" />. The url will get rendered properly using this. You may read more on this topic in the official docs.
Related
I'm working on a web application using Laravel 5.8, I'm new to Laravel framework. I would like to display PDF documents on the browser when users click on some buttons. I will allow authenticated users to "View" and "Download" the PDF documents.
I have created a Controller and a Route to allow displaying of the documents. I'm however stuck because I have a lot of documents and I don't know how to use a Laravel VIEW to display and download each document individually.
/* PDFController*/
public function view($id)
{
$file = storage_path('app/pdfs/') . $id . '.pdf';
if (file_exists($file)) {
$headers = [
'Content-Type' => 'application/pdf'
];
return response()->download($file, 'Test File', $headers, 'inline');
} else {
abort(404, 'File not found!');
}
}
}
/The Route/
Route::get('/preview-pdf/{id}', 'PDFController#view');
Mateus' answer does a good job describing how to setup your controller function to return the PDF file. I would do something like this in your /routes/web.php file:
Route::get('/show-pdf/{id}', function($id) {
$file = YourFileModel::find($id);
return response()->file(storage_path($file->path));
})->name('show-pdf');
The other part of your question is how to embed the PDF in your *.blade.php view template. For this, I recommend using PDFObject. This is a dead simple PDF viewer JavaScript package that makes embedding PDFs easy.
If you are using npm, you can run npm install pdfobject -S to install this package. Otherwise, you can serve it from a CDN, or host the script yourself. After including the script, you set it up like this:
HTML:
<div id="pdf-viewer"></div>
JS:
<script>
PDFObject.embed("{{ route('show-pdf', ['id' => 1]) }}", "#pdf-viewer");
</script>
And that's it — super simple! And, in my opinion, it provides a nicer UX for your users than navigating to a page that shows the PDF all by itself. I hope you find this helpful!
UPDATE:
After reading your comments on the other answer, I thought you might find this example particularly useful for what you are trying to do.
According to laravel docs:
The file method may be used to display a file, such as an image or PDF, directly in the user's browser instead of initiating a download.
All you need to do is pass the file path to the method:
return response()->file($pathToFile);
If you need custom headers:
return response()->file($pathToFile, $headers);
Route::get('/show-pdf/{id}', function($id) {
$file = YourFileModel::find($id);
return response()->file(storage_path($file->path));
})->name('show-pdf');
Or if file is in public folder
Route::get('/show-pdf', function($id='') {
return response()->file(public_path().'pathtofile.pdf');
})->name('show-pdf');
then show in page using
<embed src="{{ route('show-pdf') }}" type="text/pdf" >
This used to work before Django 2.0 changed url patterns from "url" to "path":
index.html
<!DOCTYPE html>
{% load static %}
<head>
<script type="text/javascript" src="{% static 'main/js/jquery-3.3.1.js' %}">
</head>
<body>
<div id='test'>
<p><button class="btn">Click Here!</button></p>
</div>
<script>
$('.btn').click(function(){
console.log('button is clicked!')
$.ajax({
url: 'main/all_json',
sucess: function(serverResponse){
console.log('success.serverResponse', serverResponse)
}
})
});
APP LEVEL urls.py
urlpatterns = [
url(r'^all_json$',views.all_json, name="all_json")
]
Project Level urls.py
app_name= "main"
urlpatterns = [
path('', include ('apps.main.urls', namespace='main')),
path('admin/', admin.site.urls),
]
views.py
def all_json(request):
return HttpResponse ('hello world!')
But now, Django 2.0 uses "path" instead of the url regex pattern. When I use path:
app_name= "name"
urlpatterns = [
path('all_json',views.all_json, name="all_json"),
]
I get the:
GET http://127.0.0.1:8000/main/all_json 404 (Not Found)
I looked in the new documentation and release notes and there are some SO answers that explain how to use it SO post 1 & SO post 2. That has been useful up to this point, where I'm unable to pass the url from the AJAX function to the "path".
I'm new to AJAX and I'm used to using the {% url main:all_json %} in Django for my actions. But with AJAX I believe I can't use this notation. Is that right?
And for some reason, the examples that I have that used url(r'^$') urlpatterns before Django 2.0 worked, but now I get a code 404 when using 'path'. Most of the questions and tutorials available are pre Django 2.0 and use url(r'^$') urlpatterns. Release notes and documentation do not mention anything about differences in working with AJAX.
My questions is the following:
Is there something else that I need to add in my template and/or urls.py to help it find the urls(get rid of the 404)?
First, url is still perfectly valid in Django 2.0. In later versions exactly the same functionality is available as re_path.
However, the problem is not there. It is that you have added a final slash in the new version where you didn't have one before. Remove it:
path('all_json', ...)
or, preferably, add it to the Ajax call:
url: 'main/all_json/',
Note finally that since the Ajax script is directly in the template file, it's absolutely possible to use the {% url %} tag there.
Try to build API's that clear and useful. Using main, all_json names are unclear. Nevertheless, let's try on your examples:
In your urls.py use main/all_json/ and name="all_json". Accoding to documentation:
There’s no need to add a leading slash, because every URL has that. For example, it’s articles, not /articles. link
...each pattern requires that the URL end with a slash. link
In your HTML template (by the way, it's maybe mistake, but you named it html.py. I advise to refactor this to somename.html), in the js block use template tag {% url "all_json"" %} like:
$.ajax({
url: '{% url "all_json" %}',
sucess: function(serverResponse){
console.log('success.serverResponse', serverResponse)
}
})
By using url template tag you can avoid many mistakes when changing urls.
Package version:1.7.2
Django version:2.1
Python version:3.7
Template pack: Bootstrap4
I have a FileField in my model and I implemented Django's FileExtensionValidator, as well as my own custom field validator to check the file size. It works, but crispy-forms doesn't display error message when these validations fail.
Model
from django.core.validators import FileExtensionValidator
class Project(models.Model):
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='projects',
)
title = models.CharField(
_('project name'),
max_length=100,
help_text=_('Required. 100 characters or fewer.'),
)
slug = models.SlugField(
_('slug'),
max_length=80,
)
created = models.DateTimeField(
_('dateTime created'),
auto_now_add=True,
)
xmlfile = models.FileField(
_('input file'),
upload_to=user_directory_path,
validators=[FileExtensionValidator(allowed_extensions=('xml',))],
help_text=_('Required. Please upload an XML file.'),
)
Form
from django.core.exceptions import ValidationError
def file_size(value):
limit = 9 * 1024 * 1024
if value.size > limit:
raise ValidationError('File too large. Size should not exceed 9 MiB.')
class ProjectForm(forms.ModelForm):
xmlfile = forms.FileField(
label='XML File Upload',
widget=forms.FileInput(attrs={'accept':'application/xml'}),
validators=[file_size],
)
class Meta:
model = Project
widgets = {
'owner': HiddenInput(),
}
Template
{% block content %}
<h1>New Project</h1>
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy }}
<input type="submit" value="Create" />
</form>
{% endblock content %}
View
class ProjectCreate(CreateView):
form_class = ProjectForm
model = Project
template_name = 'project_new.html'
success_url = reverse_lazy('my_projects_list')
def get_initial(self):
initial = super().get_initial()
initial['owner'] = self.request.user
return initial
When trying to upload an XML file less than 9M, it works and the user is brought to the success URL. But when either file format or file size is wrong, it's correct that we continue to stay on the page of project_new.html, but no error message is displayed on this page related to FileExtensionValidator or file_size().
When I change {{ form|crispy }} to {{ form.as_p }}, the validation error will be displayed on the screen. Do you know how to display validation error messages when using {{ form|crispy }}? Thank you!
According to crispy docs:
'By default when django-crispy-forms encounters errors, it fails silently, logs them and continues working if possible. A settings variable called CRISPY_FAIL_SILENTLY has been added so that you can control this behavior. If you want to raise exceptions instead of logging, telling you what’s going on when you are developing in debug mode, you can set it to:
CRISPY_FAIL_SILENTLY = not DEBUG
Besides you can check other error attributes here(documentation):
https://django-crispy-forms.readthedocs.io/en/d-0/tags.html#helper-attributes-you-can-set
Hi i just wanted to reload part of data on the website using ajax.
Website is created using template language from Django.
I have problem with using url tag inside javascript tags, code below.
urls.py
urlpatterns = [
url(r'^(?P<place_id>[\w]+)/$', place_views.place_website,
name='room_view'),
]
Website
<script type="text/javascript">
setInterval(function () {
$("#displayMoment").load("{% url room_view room_id %}");
}, 60000);
</script>
Legend
room_view is the name of the view inside views.py
room_id is the room id which is captured by regex in url pattern. But it should be in double bracket right ? {{ room_id }} ?
displayMoment is the id of the DIV.
Problem
With this i get error
NoReverseMatch at /places/2/
Reverse for '' not found. '' is not a valid view function or pattern name.
Ho does the website part should look like ?
I am trying to render the images from /WEB-INF/images/sps in the GSP page using the following code:
def index = {
def baseFolder = grailsAttributes.getApplicationContext().getResource("/").getFile().toString()
println baseFolder
def imagesFolder = baseFolder + '/images/sps'
println imagesFolder
def imageList1 = new File(imagesFolder).list()
println imageList1
def imageList = Arrays.asList(imageList1)
println imageList
imageList
//redirect(action:"displayImages", params:["imageList":imageList])
//render(view:"displayImages")
}
The controller is able to read the images from the fileSystem. But when I try to display them using the following code, the images are not coming.
index.gsp
<g:each in="${imageList}" var="image">
<img src="${resource(dir: 'images', file: image.filename)}" alt="Grails"/>
</g:each>
What mistake am I doing?
EDIT:
When I try with single image, it is working fine - I am able to view the image that is in the WEB-INF/images folder
<img src="${resource(dir: 'images', file: '1.jpg')}" alt="Grails"/>
And there is no HTML code thats getting generated for the loop code(above index.gsp code). Its just blank.
My requirement is to display all the image files that are on the file system folder.
It was simpler. You should return a model from an action as a Map: [imageList: imageList] for imageList to be available by name in GSP.
And yes, can you move images folder to web-app - is it OK that all the world can request your images via HTTP?
you are returning a list of File objects, where you will call the toString method of the file object which most likely returns the absoute file path of the file object.
this would give you something like this in the html source code
<img src="/app/images/c:\path\to\imagefile.png">
try calling
<img src="${resource(dir: 'images', file: image.name)}" alt="Grails"/>
and if that doesnt work, show us the html code that it produces.
In light of new knowledge, the above won't work. The return of File.list() is actually String[] where each string is a file name rather than a complete path.
Anyways, getting a look at the html source would shed light on what exactly gets printed out.
I suspect that maybe g:each doesn't support iterating over simple array types like String[], you could try converting it to a List.
def imageList = Arrays.asList(new File(imagesFolder).list())
Have you tried converting it to a list and using g:each with that?
why are you storing your images in WEB-INF/images? why not just images? i think the code ${resource(dir:'images')} would point to the latter.
You can't render images that are located under WEB-INF using the standard image tag. The images aren't web accessible. You'll need another controller that will stream the images back to the view for you. So something like this:
class AvatarController {
def show = {
def userInstance = User.get(params.id)
def avatarFilePath = new File(userInstance.avatarURL)
response.setContentType("application/png")
response.setContentLength(avatarFilePath.size().toInteger())
OutputStream out = response.getOutputStream();
out.write(avatarFilePath.bytes);
out.close();
}
}
And then to display this image:
<img src="/app/avatar/1234" />
I'll let you work out the conversion of this into your own needs. The key, however, is that you must stream the image back since it isn't web accessible in its current location.
You're better off just serving them outside of WEB-INF, however.
don't store data in WEB-INF, store your images in /web-app/images/
in your controller:
def baseFolder = servletContext.getRealPath("/")
def folder = baseFolder + '/images/' // web-app/images/
def imagesFolder = new File(folder)
def files = imagesFolder.listFiles().toList()
List<String> imageList = []
files.each {
imageList.add(it as String)
}
return imageList
3 in your view:
<g:each in="${imageList}" var="image">
<img src="${resource(dir: 'images', file: image)}" alt="Grails"/>
</g:each>