How to add custom layouts to Rails asset pipeline - css

I'm using different layouts for different sections of my app. In my controller I have either:
def index
#user = current_user
#orders = #user.orders.all
render layout: 'admin'
end
OR
class ProductsController < ApplicationController
layout "inventory"
#...
end
And in my assets I've got: layout_theme.css.scss, layout_login.css.scss, etc
And in my views/layouts I've got theme.html.haml, login.html.haml etc.
After making setting my app for production and using the following command to precompile the assets: bundle exec rake assets:precompile --trace
When I try to load the page, I get only two request for assets: layout_theme.css and layout_theme.js and an error.
I've tried adding this to my config/application.rb:
config.assets.precompile += %w( layout_home.css layout_home.js layout_login.css layout_login.js layout_theme.css layout_theme.js )
Which cover the additional files I'm using but it didn't work. What am I missing?
EDIT: I can see the files compiled in public/assets/layout_theme...css and public/assets/layout_theme...js but it seems rails is not referencing the compiled files in the app

Related

Rails application not loading scss

I tried many things but my navbar is not showing properly on browser, just because sccs is not loading. This application was a Rails API Application and I converted to regular application. Maybe something went wrong on this process.
Assets/Stylesheets/application.scss
// Your CSS partials
#import "components/index";
Assets/Stylesheets/Components/_index.scss
// Import your components CSS files here.
#import "navbar";
Assets/Stylesheets/Components/_navbar.scss
Environments/development.rb
config.assets.debug = true
config.assets.quiet = true
config.assets.compile = true
application.rb
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module RailsJwt
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
end
end
I am new here so I don't know which other parts of my application I should show.
Since you converted an API-only Rails application to use CSS I'm assuming you haven't configured Webpacker to handle your front end assets. This link may be helpful: https://onrails.blog/2020/02/20/using-css-in-rails-6/
The most important for you is probably adding the following to your app/views/application.html.erb file:
<%= stylesheet_link_tag 'application', media: 'all' %>
If you're using Webpacker, adjust accordingly. Also, I don't think Rails 6 ships with the sprockets gem so you may have to add that to your Gemfile, unless you plan on using Webpacker.

Pyexcel, loading a file to create a book in memory

This is solved; thanks to #vmontco's solution: I was missing MEDIA_URL, now it works perfectly.
----------original question below-----------
I welcome suggestions from every angle; I am fairly new to Django and Python. I'm sure I am missing something simple.
Using a Model Form, with a FileField, I upload and save an Excel file to a folder structure under MEDIA_ROOT. This works.
I want to read that same file later to perform operations using Pyexcel. This is where I am stuck. I am attempting to upload the file using the FileField stored in the DB.
This is where I have problems, and I am not sure if am misunderstanding MEDIA_ROOT, or some other aspect of Django.
When I pass the pk to the 2nd view, I then instantiate an object based on the Model. It has the FileField 'docfile', which I am trying to use to access the file to do some operations using Pyexcel,
here is the FileField declaration from models.py:
docfile = models.FileField(
verbose_name="Choose file to upload:",
upload_to='Excel_CSV_Assets/%Y/%m/%d')
EDIT: If I hard-code the pth to the file like this, everything works, including operations afterwards:
thedocfile='site_static/site/original_assets/Excel_CSV_Assets/2016/04/23/Animals_oglc4DV.xlsx'
book=pyexcel.get_book(file_name=thedocfile)
:END OF EDIT
Here is the code from the 2nd view, where I attempt to read the file into memory, and make a 'book' class object using Pyexcel. I am stuck here:
asset = Excel_CSV_Asset.objects.get(id=assetid)
book=pyexcel.get_book(file_name=asset.docfile)
Here is my error description:
Here is the info right at where my code breaks:
Although it says "Wrong filename", I can see the file is in the folder:
I'm able to open the file by double-clicking; the file is not corrupted.
EDIT:
If I cast the 'asset.docfile' to str, like so:
asset = Excel_CSV_Asset.objects.get(id=assetid)
book=pyexcel.get_book(file_name=str(asset.docfile))
I get a different error:
[Errno 2] No such file or directory: 'Excel_CSV_Assets/2016/04/23/Animals_oglc4DV.xlsx'
...but this is the correct directory, located beneath the MEDIA_ROOT file structure.
Here is settings.py MEDIA_ROOT:
MEDIA_ROOT = 'site_static/site/original_assets/'
Here is urls.py:
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^e/', include('excel_to_mongo.urls')),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Here is the url.py of that app:
url(r'^efactory/(?P<assetid>\d+)/$', 'display_sheet_column_choices', {}),
I think your problem is that you don't fully understand the media files management with Django.
What are media files?
Media files are all the files that are user-uploaded (at running time).
You must not mistake them with Static files that are assets needed by your project to work and that you add at development time (CSS, background picture and JS files for instance).
You shouldn't mix them because they are managed differently by the server and that it could lead to security problems (cf. the warning here):
Static files management :
You put your static files as a part of the code either in one static subdirectory from the installed django applications, either in one of the locations you added to STATICFILES_DIRS.
Static files have to be gathered before starting the server by calling ./manage.py collectstatic, this command will collect (copy) the static files into the a directory (STATIC_ROOT's value).
You then have to set STATIC_URL to choose with wich url you should serve your static files. An usual choice would be /static/. To access the static file you should then try to reach /static/path/to/static/file/in/static_root/dir.
Media files management :
Your media files are added at running time. They are stored in the MEDIA_ROOT location that has to be an absolute path. Hence the fact I suggested you to join the BASE_DIR value (an absolute path) and the subdir you would choose with something like :
MEDIA_ROOT = os.path.join(BASE_DIR, "/media/subdir")
You then have to set an URL for your media files, by using the MEDIA_URL variable. To access your media files, the urls will start with the value you choose :
MEDIA_URL = '/media/'
Then, add this to your urls.py file :
if settings.DEBUG:
urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
With the current example, your mymediafile.txt will be located at /path/to/your/project/media/subdir/path/in/media/root/mymediafile.txt and served at http://127.0.0.1:8000/media/path/in/media/root/mymediafile.txt.
But this is suitable only for a development use as told here. And this would work only for DEBUG == TRUE
For a production use, you should consider deploying your media files with your http server (apache for instance).
Conclusion :
Take the time to understand this. Because I suspect you don't really understood what you did and this lack of understanding could lead to future bugs and errors.

Django: Image referenced in CSS showing locally but not on Heroku

I'm working in Django, deploying via Heroku, trying to use WhiteNoise for static assets.
I have a CSS file which refers to an image to use as a background:
body { background: white url("images/nyc.jpg") left top; }
When I load the page locally, the background image shows up perfectly.
However, when I deploy to Heroku (using git push from my local machine), the background image does NOT show up on the page. I get this error message when I do the git push to Heroku:
Traceback (most recent call last):
whitenoise.django.MissingFileError: The file 'multiblog/images/nyc.jpg' could not be found with <whitenoise.django.GzipManifestStaticFilesStorage object at 0x7fab5c4fb210>.
The CSS file 'multiblog/style.css' references a file which could not be found:
Please check the URL references in this CSS file, particularly any
relative paths which might be pointing to the wrong location.
Here are the relevant lines from my project level settings.py (happy to share more from it if needed):
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/
BASE_DIR2 = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT = 'staticfiles'
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR2, 'static'),
]
# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise
application = get_wsgi_application()
application = DjangoWhiteNoise(application)
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'
And here's a picture of the file structure.
In the screenshot you provided, the filename is nyc.JPG, while your code references nyc.jpg. Filenames are case-sensitive :)
Just run
python manage.py collectstatic
see more : collectstatic , Django and Static Assets

Creating a Bundle outside src/

I have created a bundle with symfony 2.3 but in this case (cause my teacher asked to me) outside src/ folder so I have ../symfony/fuentes/NameBundle instead of ../symfony/src/NameBundle. The new line appears in AppKernel and my new bundle appears on routing.yml, but when I try to launch the server
Bundle generation
Generating the bundle code: OK
Checking that the bundle is autoloaded: FAILED
Confirm automatic update of your Kernel [yes]?
Enabling the bundle inside the Kernel: OK
Confirm automatic update of the Routing [yes]?
Importing the bundle routing resource: OK
The command was not able to configure everything automatically.
You must do the following changes manually.
- Edit the composer.json file and register the bundle
namespace in the "autoload" section:
I have edit autoload and tried a lot of things (looking for here) but it appears the same error always.
C:\Users\Akenateb\Documents\UOC\AULAMENTOR\Symfony>php app/console server:run 127.0.0.1:8080
PHP Fatal error: Class 'AulaMentor\ExdosBundle\AulaMentorExdosBundle' not found in C:\Users\Akenateb\Documents\UOC\AULAMENTOR\Symfony\app\AppKernel.php on line 20
Can someone help me? I'm really stuck with it.
Thanks in advance.
First of all I want to thanks to the people who has answered. Here is what we have to do if we want to create a bundle outside 'src' folder, for example in 'fuentes' > '..Symfony/fuentes'.
If you have create with 'generate:bundle' I suggest you accept when the generator ask you if you want to create the complete structure, if you have create a bundle with generator goto step 3.
1- Make sure you have registered bundle in AppKernel and It exists there a line like this:
new YourProject\NameprojectBundle\YourProjectNameprojectBundle(),
2- Make sure you have adding a route to your app/config/routing and 'routing.yml' has you new bundle route, like this (you can add a prefix to your url, in this case fuentes):
your_project_name:
resource: "#YourProjectNameprojectBundle/Resources/config/routing.yml"
prefix: /fuentes
3- We edit 'app/autoload.php' and we add this line:
$loader->add('YourProject',realpath(__DIR__.'/../fuentes'));
Finally we can update assets doing with command line:
php app/console assets:install web
Hope it helps to someone.
Best Regards.
Do exactly as said in the comment:
Edit the composer.json file and register the bundle namespace in the "autoload" section
The src folder is automatically loaded using PSR convention. If you set classes outside of the src folder, they have to be declared as well.
In your composer.json file you can add another element in the autoload section. Here's an example that I think will work for your use case:
"autoload": {
"psr-0": {
"NameBundle\\": "fuentes/",
"": "src/"
}
},
Here's how to add simply another bundle outside your Symfony project, when we don't want to mess up with composer.json file.
The 2 first points are the same as in the Michael J.'s answer. Now to the 3rd point:
Say, we need to add a OurCompany/SomeBundle residing in the other project, which relative path is ../../OtherProject/src/OurCompany/SomeBundle to CurrentProject/app dir.
So we add this bundle to the CurrentProject application this way:
$loader->add('OurCompany\\SomeBundle', realpath(__DIR__.'/../../OtherProject/src'));
PLEASE NOTICE how the slashes and backslashes should be used (the remaining / or \ at the end doesn't matter, it's smart enough to figure it out).
And for the whole namespace to be loaded (all bundles namespaces in the other project available in CurrentProject):
$loader->add('OurCompany', realpath(__DIR__.'/../../OtherProject/src'));

Django Pipeline, Heroku, and SASS

I've been trying to get django-pipeline setup so that I can compile and concat my assets. I would also like to remove the compiled css files from my repository to avoid merge conflicts in pull requests.
I've been trying to get django-pipeline to compile the files as part of the deploy process but can't figure this out. I use SASS to write my CSS. My pipeline settings look like this:
STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
PIPELINE_CSS = {
'main': {
'source_filenames': (
'sass/blah.scss',
'sass/main.scss',
),
'output_filename': 'css/main.css',
'extra_context': {
'media': 'screen',
},
},
}
PIPELINE_COMPILERS = (
'pipeline.compilers.sass.SASSCompiler',
)
This works great locally, and generates .css files in my /sass folder, which are then combined to make the main.css file. If I check those CSS files into my git repository and push to Heroku, it also works fine. However, if I ignore them, which I would like to do so that I'm not committing compiled files, then django-pipeline can't find the files to combine. I'm not sure how I can get the sass compilation working on Heroku and I can't find anything about it.
I can provide more information about my setup if needed, hopefully somebody knows something about this!
OK, here's how I got this working using Compass to compile my SASS files.
Use multiple Heroku buildpacks - Heroku Buildpack Multi
Put the following in your .buildpacks file
https://github.com/heroku/heroku-buildpack-ruby.git
https://github.com/heroku/heroku-buildpack-nodejs
https://github.com/heroku/heroku-buildpack-python.git
Create a Gemfile with compass and any other requirements you have. Here's mine:
source 'https://rubygems.org'
ruby '1.9.3'
gem 'bootstrap-sass'
gem 'compass'
Create a config.rb file. Here's mine. As you can see it, requires the bootstrap-sass that I included in my Gemfile:
# Require any additional compass plugins here.
require 'bootstrap-sass'
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "app_folder/static/css"
sass_dir = "app_folder/static/sass"
images_dir = "app_folder/static/images"
output_style = :compact
more details on config.rb can be found here
Install node packages (django-pipeline wants yuglify). You'll need a package.json file:
{
"dependencies": {
"yuglify": "0.1.4"
},
"engines": {
"node": "0.10.x",
"npm": "1.3.x"
},
"repository": {
"type": "git",
"url": "your repo url"
}
}
almost ready...
when Heroku runs the ruby buildpack, it will look for a rake task called assets:precompile. So now you'll need to add a Rakefile with the following:
namespace 'assets' do
desc 'Updates the stylesheets generated by Sass/Compass'
task :precompile do
print %x(compass compile --time)
end
end
this will put compile your stylesheets. You need to make sure you set the output (back in config.rb) to the place that django-pipeline is looking for CSS files (shown in the original question).
You should get rid of this part in the original question as django-pipeline isn't compiling your SASS for you:
PIPELINE_COMPILERS = (
'pipeline.compilers.sass.SASSCompiler',
)
That should be it! Deploys should just work now, and it didn't really add a significant amount of time to my deploy process.
All in all, it amounts to a lot of setup, but for me it was well worth it as I no longer have to commit compiled files into the repository, which was causing a lot of merge conflicts when working with branches and pull requests.
I would like to figure out how to do this using only two buildpacks (obviously only one would be ideal but I don't know if it's possible). The problem is trying to find binary paths so that pipeline can do it's thing when it doesn't find the defaults. I'm not sure if the reason I can't do this is because of how Heroku is installing things, or because there is a bug in django-pipeline, but right now this is good enough for me.
If you try this and it doesn't work for you, let me know, if I missed something I'm happy to make updates.
I don't want to take away from your excellent solution, but I tried this today and found a few differences that made things simpler for me - likely due to updates in django-pipeline and/or Heroku. My full solution is below, in case anyone else comes looking.
Add the 3 buildpacks to Heroku:
heroku buildpacks:set https://github.com/heroku/heroku-buildpack-ruby.git
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-nodejs
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-python.git
Add django-pipeline and django-pipeline-compass to requirements.txt:
django-pipeline==1.5.2
django-pipeline-compass==0.1.5
Create a Gemfile to install Sass:
source 'https://rubygems.org'
ruby '2.1.5'
gem 'bootstrap-sass'
Create a package.json file to install Yuglify:
{
"dependencies": {
"yuglify": "0.1.4"
},
"engines": {
"node": "0.10.x",
"npm": "1.4.x"
}
}
I did not need a Rakefile or config.rb.
For reference, here are relevant settings from my settings.py:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '_generated_media')
STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'pipeline.finders.PipelineFinder',
)
PIPELINE_COMPILERS = (
'pipeline_compass.compiler.CompassCompiler',
)
PIPELINE_YUGLIFY_BINARY = os.path.join(BASE_DIR, 'node_modules', '.bin', 'yuglify')
And I also had to add this entry to urls.py:
url(r'^static/(?P<path>.*)$', serve, kwargs={'document_root': settings.STATIC_ROOT})
Hope it helps someone!
You may need to set PIPELINE_SASS_BINARY so that django-pipeline can find your SASS compiler.
You can use the libsass compiler for django-pipeline that uses Sass packaged as a Python package:
pip install libsasscompiler
Update your config:
PIPELINE['COMPILERS'] = (
'libsasscompiler.LibSassCompiler',
)
The default Yuglify compressor is also a problem on Heroku, which you can temporarily overcome by disabling it. This is my config for enabling Sass on Django for example:
PIPELINE = {
'COMPILERS': (
'libsasscompiler.LibSassCompiler',
),
'STYLESHEETS': {
'main': {
'source_filenames': (
'styles/main.scss',
),
'output_filename': 'css/main.css'
},
},
# disable the default Yuglify compressor not available on Heroku
'CSS_COMPRESSOR': 'pipeline.compressors.NoopCompressor',
'JS_COMPRESSOR': 'pipeline.compressors.NoopCompressor'
}
The longer-term solution would be to move towards a JS-only build toolchain (as most projects are doing). Rails integrates pretty nicely with Webpack for example and is maintained by the same team. Until that happens in Django (if ever) and trickles into the Heroku Python builpack, you can use Heroku's multiple buildpacks and add an official Node buildpack step that runs npm install; npm run build for you.

Resources