New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed #12978: Added support for RSS feed stylesheets. #18120
base: main
Are you sure you want to change the base?
Fixed #12978: Added support for RSS feed stylesheets. #18120
Conversation
accd5e1
to
06bd3c7
Compare
e258b5e
to
4236ef0
Compare
Nice to see this request popping up again after 14 years 😂 https://code.djangoproject.com/ticket/12978 |
docs/ref/contrib/syndication.txt
Outdated
from django.contrib.syndication.views import Feed | ||
from django.urls import reverse | ||
|
||
|
||
class FeedWithStylesheetView(Feed): | ||
... # author, etc. | ||
xsl_stylesheet_url = reverse("your-custom-view-name") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't realize there was already a ticket for this (I did search before creating one but clearly not well enough). Do you think those are reasonable limitations @yuvadm ? Your proposal was for multiple RSS stylesheets but from the little I've seen, XSL seems like a more widespread format, do you agree? |
@bmispelon after so much time, I hardly remember why I added support for multiple stylesheets, I was probably aiming for maximum flexibility. If I were to re-implemented, in terms of API design, I'd probably be liberal in accepting both single and iterable stylesheets in one arg, but unsure if that meets Django code standards. Anyway nice to see great minds thinking alike 😄 |
Hey @bmispelon, I don't mean to rush you but wanted to mention that when this is ready for a review, please update the ticket flags in trac so it shows in the review queue. Thanks! |
c4de8d5
to
66e1eac
Compare
Thanks for the heads up, I hadn't notice that the original ticket still had the "patch needs improvement" flag. I've squashed and rebased this PR and updated the flags on the ticket, it should be ready to review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very thorough PR, looks generally good to me, although I'm not familiar with XSLT stylesheets.
I think a css stylesheet should also be supported 🤔 |
66e1eac
to
8ecba20
Compare
I'm a bit puzzled about the test failure. The failing test passes for me locally 🤔 |
django/contrib/syndication/views.py
Outdated
@@ -160,6 +160,7 @@ def get_feed(self, obj, request): | |||
feed_copyright=self._get_dynamic_attr("feed_copyright", obj), | |||
feed_guid=self._get_dynamic_attr("feed_guid", obj), | |||
ttl=self._get_dynamic_attr("ttl", obj), | |||
stylesheet=self._get_dynamic_attr("stylesheet", obj), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to support having multiple stylesheets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to keep the API simple because I assume most people only really have a single stylesheet. I pushed a separate commit that adds documentation (and tests) for how to extend the Feed
class to support multiple stylesheets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you 👍 that's a lovely addition
This is what I'm thinking.
If we think we might support multiple in the future, we'd want to deprecate stylesheet
and add stylesheets
. In that case it's easier to do now.
Otherwise, as you have documented how to do this in your own code, we'd be recommending users that it should be in your own code and that's a decision to not add it into Django.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added support for multiple stylesheets (I tried to contain it to a single commit so you can see the diff easier), it didn't add much complexity and your argument about future-proofing against a possible deprecation cycle is convincing.
I did opt to support both a single stylesheet and a list of them, which does make the code (and the documentation) a bit more complex, but I thought it was worth it so that we can offer a simpler API when people only want to use a single stylesheet. If you think that tradeoff is not worth it, I'm also happy to make the API list-only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have too strong of an opinion here. I personally would have just supported lists/tuples (a bit like admin inlines) but I don't mind having this. We can get another opinion 👍
855b5f2
to
a5ecf0f
Compare
docs/ref/utils.txt
Outdated
|
||
.. versionadded:: 5.1 | ||
|
||
.. class:: Stylesheet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realise this is how it is documented below, but we mostly don't document the __init__
method this way and do things like:
.. class:: Stylesheet | |
.. class:: Stylesheet(url, mimetype="", media="screen") |
And then define each of the arguments like:
.. attribute:: url
The URL of the stylesheet. This argument is required.
I quite like this as you can then link to the specific arguments.
Example: https://docs.djangoproject.com/en/5.0/ref/contrib/postgres/aggregates/#arrayagg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, it's much more usable that way. One thing to note (it might not be a huge deal but I thought I'd flag it just in case): while media
is indeed a true attribute, the other two are @property
so if someone ever tries to instantiate a Stylesheet
then change the attributes afterwords then they'll get an error. It should be possible to add setters to make the attributes work transparently, but I wasn't sure if it was worth the added complexity. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong, but I think we do this mostly for formatting rather than to tell the user it is an attribute vs a property 🤔 so I don't think the docs should drive any requirement for setters
django/contrib/syndication/views.py
Outdated
@@ -160,6 +160,7 @@ def get_feed(self, obj, request): | |||
feed_copyright=self._get_dynamic_attr("feed_copyright", obj), | |||
feed_guid=self._get_dynamic_attr("feed_guid", obj), | |||
ttl=self._get_dynamic_attr("ttl", obj), | |||
stylesheet=self._get_dynamic_attr("stylesheet", obj), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you 👍 that's a lovely addition
This is what I'm thinking.
If we think we might support multiple in the future, we'd want to deprecate stylesheet
and add stylesheets
. In that case it's easier to do now.
Otherwise, as you have documented how to do this in your own code, we'd be recommending users that it should be in your own code and that's a decision to not add it into Django.
647fb20
to
4426a29
Compare
4426a29
to
96a77c2
Compare
stylesheets = "/link/to/stylesheet.xsl" | ||
stylesheets = feedgenerator.Stylesheet("/link/to/stylesheet.xsl") | ||
stylesheets = ["/stylesheet1.xsl", "stylesheet2.xls"] | ||
stylesheets = [ | ||
feedgenerator.Stylesheet("/stylesheet1.xsl"), | ||
feedgenerator.Stylesheet("/stylesheet2.xsl"), | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you've documented the different types in the feed stylesheet docs so I would pick your favourite here as I think the provide one of the following three.
comment doesn't quite work and I think this is more to document the _get_dynamic_attr
behaviour 🤔
|
||
Beware that some control characters are | ||
`not allowed <https://www.w3.org/International/questions/qa-controls>`_ in | ||
XML documents. If your content has some of them, you might encounter a | ||
:exc:`ValueError` when producing the feed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're supposed to add this here as well maybe
.. versionchanged:: 5.1
The ``stylesheets`` argument was added.
You can add styling to your RSS feed using a stylesheet (typically in the XSLT_ | ||
or CSS formats) by setting the ``stylesheets`` attribute on the feed class. | ||
|
||
This can be a hardcoded URL:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can use .. code-block:: python
to make these sections prettier
@@ -158,7 +158,9 @@ Minor features | |||
:mod:`django.contrib.syndication` | |||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
|
|||
* ... | |||
* All :class:`~django.utils.feedgenerator.SyndicationFeed` classes now support | |||
a ``stylesheet`` attribute. If specified, an ``<? xml-stylesheet ?>`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a ``stylesheet`` attribute. If specified, an ``<? xml-stylesheet ?>`` | |
a ``stylesheets`` attribute. If specified, an ``<? xml-stylesheet ?>`` |
docs/ref/utils.txt
Outdated
|
||
.. versionadded:: 5.1 | ||
|
||
.. class:: Stylesheet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong, but I think we do this mostly for formatting rather than to tell the user it is an attribute vs a property 🤔 so I don't think the docs should drive any requirement for setters
django/contrib/syndication/views.py
Outdated
@@ -160,6 +160,7 @@ def get_feed(self, obj, request): | |||
feed_copyright=self._get_dynamic_attr("feed_copyright", obj), | |||
feed_guid=self._get_dynamic_attr("feed_guid", obj), | |||
ttl=self._get_dynamic_attr("ttl", obj), | |||
stylesheet=self._get_dynamic_attr("stylesheet", obj), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have too strong of an opinion here. I personally would have just supported lists/tuples (a bit like admin inlines) but I don't mind having this. We can get another opinion 👍
Trac ticket number
ticket-35420 (duplicate)
ticket-12978 (the actual original ticket)
Branch description
To simplify the implementation I decided to restrict the types of stylesheets to XSL (it lets me hardcode the type of the stylesheet), it seems to be the de-facto standard anyway.
Checklist
main
branch.