Changeset 1701

Show
Ignore:
Timestamp:
07/21/08 13:53:50 (12 months ago)
Author:
JensDiemer
Message:

blog: add Feed function ;)

Location:
trunk/pylucid
Files:
2 added
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/pylucid/media/PyLucid/internal_page/blog/display_blog.css

    r1691 r1701  
    1 fieldset { 
     1.blog fieldset { 
    22  padding: 0.2em 0em 0.5em 1em; 
    33  margin: 0px; 
     
    66  border-top: 1px solid #777; 
    77} 
    8 legend { 
    9   font-size: 0.8em; 
    10     padding: 2px; 
    11     border: 1px solid #000; 
    12     background-color: #FFF; 
     8.blog legend { 
     9  font-size: 0.9em; 
     10  border: 1px solid #333; 
     11  background-color: #FFF; 
     12  padding: 0.2em 1em 0.2em 1em; 
     13  margin: 0px; 
     14  margin-left: 1em; 
    1315} 
    1416/* -------------------------------------------------------------------------- */ 
    15 .entry { 
     17.blog fieldset.entry { 
    1618  background-color: #fff; 
    1719  margin: 1em 0 1em 0; 
    1820  padding: 0; 
    1921} 
    20 .headline { 
     22.blog .headline { 
    2123  background-color: #f5f5f5; 
    22   padding: 0.5em; 
    23   margin-left: 1em; 
    2424} 
    25 .entry, .headline { 
     25.blog .headline a { 
     26  text-decoration: none; 
     27} 
     28.blog fieldset.entry, .blog .headline { 
    2629  border: 1px solid #888; 
    2730} 
    28 .headline { 
    29   padding: 0 0.5em 0 0.5em; 
    30   font-size: 1.5em; 
    31 } 
    32 .content { 
    33   margin: 0.1em 0.5em 0.1em 0.5em; 
     31.content, .comment_link { 
     32  padding: 0.1em 0.5em 0.1em 0.5em; 
    3433} 
    3534/* -------------------------------------------------------------------------- */ 
     
    6160} 
    6261/* -------------------------------------------------------------------------- */ 
    63 .taglist { 
     62.blog fieldset.taglist { 
    6463  background-color: #eee; 
    6564} 
    66 .taglist legend, .dateinfo { 
     65.dateinfo { 
    6766  color: #666; 
    6867} 
    69 .taglist a { 
     68.blog .taglist { 
    7069  text-decoration: none; 
    7170  font-size: 0.9em; 
    7271} 
    73 .taglist a:hover { 
     72.blog .taglist a:hover { 
    7473  text-decoration: underline; 
    7574} 
     
    147146} 
    148147/* -------------------------------------------------------------------------- */ 
     148.feeds ul, .feeds * { 
     149  list-style-type:none; 
     150  padding: 0px; 
     151  margin: 0px; 
     152} 
     153.feed_type, .feed_type li { 
     154  display:inline; 
     155} 
     156/* -------------------------------------------------------------------------- */ 
  • trunk/pylucid/media/PyLucid/internal_page/blog/display_blog.html

    r1691 r1701  
    7373            <fieldset class="comments"><legend>Leave a comment</legend> 
    7474            <script type="text/javascript"> 
    75                    document.write('<form method="post" action=".">'); 
     75                   document.write('<fo'+'rm met'+'hod="po'+'st" act'+'ion=".">'); 
    7676            </script> 
    7777            <ul id="input_fields"> 
    7878                    {% for field in add_comment_form %} 
    7979                        <li title="{{ field.help_text }}" class="{{ field.html_name }}"> 
    80                         <label for="{{ field.auto_id }}">{{ field.label }} :</label> 
     80                        <label for="{{ field.auto_id }}">{{ field.label }}:</label> 
    8181                        {{ field }} 
    8282                        <span class="field_help_text">{{ field.help_text }}</span> 
     
    8787            <noscript><h2 style="color:red;">JavaScript required!</h2></noscript> 
    8888            <script type="text/javascript"> 
    89                    document.write('<input type="submit" value="{% trans 'submit' %}" />'); 
    90                    document.write('</form>'); 
     89                   document.write('<in'+'put type="sub'+'mit" value="{% trans 'submit' %}" />'); 
     90                   document.write('</fo'+'rm>'); 
    9191            </script> 
    9292            </fieldset> 
     
    9696{% endfor %} 
    9797 
     98<fieldset class="feeds"><legend>{% trans 'available syndication feeds' %}</legend> 
     99    {% regroup feed_info|dictsort:"feed_type" by feed_type as feeds_grouped %} 
     100    <ul> 
     101    {% for feed in feeds_grouped %} 
     102        <li class="grouper"><strong>{{ feed.grouper }}:</strong> 
     103        <ul class="feed_type"> 
     104            {% for item in feed.list %} 
     105            <li><a href="{{ item.url }}">{{ item.title_info }}</a></li> 
     106            {% endfor %} 
     107        </ul> 
     108        </li> 
     109    {% endfor %} 
     110    </ul> 
     111</fieldset> 
     112 
    98113{% if back_url %}<p><a href="{{ back_url }}" class="action_link">Display all blog entries</a></p>{% endif %} 
    99114{% if create_url %}<p><a href="{{ create_url }}" class="action_link">create a new blog entry</a></p>{% endif %} 
  • trunk/pylucid/media/PyLucid/internal_page/blog/edit_blog_entry.html

    r1686 r1701  
    44    {% for field in form %} 
    55        <li title="{{ field.help_text }}" class="{{ field.html_name }}"> 
    6         <label for="{{ field.auto_id }}">{{ field.label }} :</label> 
     6        <label for="{{ field.auto_id }}">{{ field.label }}:</label> 
    77        {{ field }} 
    88        <span class="field_help_text">{{ field.help_text }}</span> 
  • trunk/pylucid/media/PyLucid/internal_page/blog/edit_comment.html

    r1688 r1701  
    44    {% for field in form %} 
    55        <li title="{{ field.help_text }}" class="{{ field.html_name }}"> 
    6         <label for="{{ field.auto_id }}">{{ field.label }} :</label> 
     6        <label for="{{ field.auto_id }}">{{ field.label }}:</label> 
    77        {{ field }} 
    88        <span class="field_help_text">{{ field.help_text }}</span> 
  • trunk/pylucid/PyLucid/plugins_internal/blog/blog.py

    r1699 r1701  
    2121__version__= "$Rev:$ Alpha" 
    2222 
    23 import datetime 
    24  
    25 from django.db import models 
     23# from python core 
     24import os, datetime, posixpath 
     25 
     26# from django 
    2627from django.conf import settings 
    27 from django import newforms as forms 
     28from django.http import HttpResponse 
    2829from django.utils import feedgenerator 
    2930from django.core.mail import send_mail 
    30 from django.contrib.auth.models import User 
    3131from django.utils.safestring import mark_safe 
    3232from django.utils.encoding import force_unicode 
    3333from django.utils.translation import ugettext as _ 
    34  
    3534from django.utils import feedgenerator 
    3635from django.contrib.syndication.feeds import Feed, FeedDoesNotExist 
    3736 
    38  
     37# from PyLucid 
     38from PyLucid.tools.utils import escape 
    3939from PyLucid.system.BasePlugin import PyLucidBasePlugin 
    40 from PyLucid.tools.content_processors import apply_markup 
    41 from PyLucid.tools.newforms_utils import StripedCharField 
    4240from PyLucid.tools.utils import escape_django_tags 
    4341from PyLucid.system.page_msg import PageMessages 
    44 from PyLucid.models.Page import MARKUPS 
    45  
     42 
     43# from blog plugin 
     44from PyLucid.plugins_internal.blog.forms import BlogCommentForm, \ 
     45                                                AdminCommentForm, BlogEntryForm 
     46from PyLucid.plugins_internal.blog.models import BlogComment, BlogTag, BlogEntry 
    4647from PyLucid.plugins_internal.blog.blog_cfg import DONT_CHECK, REJECT_SPAM, \ 
    4748                                                                    MODERATED 
     49 
     50#______________________________________________________________________________ 
     51 
     52PLUGIN_MODELS = (BlogComment, BlogTag, BlogEntry,) 
    4853 
    4954# Don't send mails, display them only. 
     
    5156MAIL_DEBUG = False 
    5257 
    53 """ 
    54 AVAILABLE_FEEDS = { 
    55     ENTRIES_FEED = "all_blog_entries" 
    56     COMMENTS_FEED = "all_blog_comments" 
    57 } 
    58  
    59 FEED_FORMATS = ( 
    60     { 
    61         "file_ext": "rss", 
    62         "generator": feedgenerator.Rss201rev2Feed, 
    63     }, 
    64     { 
    65         "file_ext": "atom", 
    66         "generator": feedgenerator.Atom1Feed, 
    67     }, 
    68 ) 
    69 """ 
    70  
    7158#______________________________________________________________________________ 
    72  
    73 class BlogComment(models.Model): 
     59# FEEDS 
     60 
     61# Don't response the RSS/Atom feed, display it only 
     62#FEED_DEBUG = True 
     63FEED_DEBUG = False 
     64 
     65ENTRIES_FEED_NAME = u"entries" 
     66COMMENTS_FEED_NAME = u"comments" 
     67TAG_FEED_PREFIX = u"tag_" # The tag slug would be appended! 
     68 
     69# All file endings must be lower case and without "." ! 
     70RSS = u"rss" 
     71ATOM = u"atom" 
     72 
     73#______________________________________________________________________________ 
     74 
     75class FeedInfo(list): 
    7476    """ 
    75     comment from non-registered users 
     77    A list of feed information. 
     78    I a normal list with one spectial add method. 
     79    Contains a dict for every feed info. 
    7680    """ 
    77     blog_entry = models.ForeignKey("BlogEntry") 
    78  
    79     ip_address = models.IPAddressField(_('ip address'),) 
    80     person_name = models.CharField( 
    81         _("person's name"), max_length=50, 
    82         help_text=_("Your full name (will be published) (required)"), 
    83     ) 
    84     email = models.EmailField( 
    85         _('e-mail address'), 
    86         help_text=_("Only for internal use. (will not be published) (required)"), 
    87     ) 
    88     homepage = models.URLField( 
    89         _("homepage"), help_text = _("Your homepage (optional)"), 
    90         verify_exists = False, max_length = 200, 
    91         null=True, blank=True 
    92     ) 
    93  
    94     content = models.TextField(_('content'), max_length=3000) 
    95  
    96     is_public = models.BooleanField(_('is public')) 
    97  
    98     createtime = models.DateTimeField( 
    99         auto_now_add=True, help_text="Create time", 
    100     ) 
    101     lastupdatetime = models.DateTimeField( 
    102         auto_now=True, help_text="Time of the last change.", 
    103     ) 
    104     createby = models.ForeignKey( 
    105         User, editable=False, 
    106         help_text="User how create the current comment.", 
    107         null=True, blank=True 
    108     ) 
    109     lastupdateby = models.ForeignKey( 
    110         User, editable=False, 
    111         help_text="User as last edit the current comment.", 
    112         null=True, blank=True 
    113     ) 
    114  
    115     class Admin: 
    116         pass 
    117  
    118     class Meta: 
    119         app_label = 'PyLucidPlugins' 
    120  
    121  
    122  
    123 class BlogCommentForm(forms.ModelForm): 
    124     """ 
    125     Add a new comment. 
    126     """ 
    127     person_name = forms.CharField( 
    128         min_length=4, max_length=50, 
    129         help_text=_("Your name."), 
    130     ) 
    131     content = StripedCharField( 
    132         label = _('content'), min_length=5, max_length=3000, 
    133         help_text=_("Your comment to this blog entry."), 
    134         widget=forms.Textarea(attrs={'rows': '15'}), 
    135     ) 
    136  
    137     class Meta: 
    138         model = BlogComment 
    139         # Using a subset of fields on the form 
    140         fields = ('person_name', 'email', "homepage") 
    141  
    142  
    143 class AdminCommentForm(BlogCommentForm): 
    144     """ 
    145     Form for editing a existing comment. Only for Admins 
    146     """ 
    147     class Meta: 
    148         model = BlogComment 
    149         fields = ( 
    150             'ip_address', 'person_name', 'email', "homepage", 
    151             "content", "is_public", 
    152             "createtime", "lastupdatetime", "createby", "lastupdateby" 
    153         ) 
     81    def add(self, title_info, url_parts): 
     82        assert(isinstance(url_parts, (list,tuple))) #Needed? 
     83        url_start = posixpath.join(*url_parts) 
     84 
     85        list.append(self, { 
     86            "feed_type": RSS, 
     87            "title_info": title_info, 
     88            "url": "%s.%s" % (url_start, RSS), 
     89        }) 
     90        list.append(self, { 
     91            "feed_type": ATOM, 
     92            "title_info": title_info, 
     93            "url": "%s.%s" % (url_start, ATOM), 
     94        }) 
     95 
    15496 
    15597#______________________________________________________________________________ 
    156  
    157  
    158 class BlogTagManager(models.Manager): 
    159     """ 
    160     Manager for BlogTag model. 
    161     """ 
    162     def get_or_creates(self, tags_string): 
    163         """ 
    164         split the given tags_string and create not existing tags. 
    165         returns a list of all tag model objects and a list of all created tags. 
    166         """ 
    167         tag_objects = [] 
    168         new_tags = [] 
    169         for tag_name in tags_string.split(" "): 
    170             tag_name = tag_name.strip().lower() 
    171             try: 
    172                 tag_obj = self.get(name = tag_name) 
    173             except self.model.DoesNotExist: 
    174                 new_tags.append(tag_name) 
    175                 tag_obj = self.create(name = tag_name, slug = tag_name) 
    176  
    177             tag_objects.append(tag_obj) 
    178  
    179         return tag_objects, new_tags 
    180  
    181  
    182 class BlogTag(models.Model): 
    183  
    184     objects = BlogTagManager() 
    185  
    186     name = models.CharField(max_length=255, core=True, unique=True) 
    187     slug = models.SlugField( 
    188         unique=True, prepopulate_from=('tag',), max_length=120 
    189     ) 
    190  
    191     def __unicode__(self): 
    192         return self.name 
    193  
    194     class Admin: 
    195         pass 
    196  
    197     class Meta: 
    198         app_label = 'PyLucidPlugins' 
    199  
    200  
    201 #______________________________________________________________________________ 
    202  
    203 class BlogEntry(models.Model): 
    204     """ 
    205     A blog entry 
    206     """ 
    207     headline = models.CharField(_('Headline'), 
    208         help_text=_("The blog entry headline"), max_length=255 
    209     ) 
    210     content = models.TextField(_('Content')) 
    211     markup = models.IntegerField( 
    212         max_length=1, choices=MARKUPS, 
    213         help_text="the used markup language for this entry", 
    214     ) 
    215  
    216     tags = models.ManyToManyField(BlogTag, blank=True) 
    217  
    218     is_public = models.BooleanField( 
    219         default=True, help_text="Is post public viewable?" 
    220     ) 
    221  
    222     createtime = models.DateTimeField(auto_now_add=True) 
    223     lastupdatetime = models.DateTimeField(auto_now=True) 
    224     createby = models.ForeignKey(User, 
    225         editable = False, 
    226     ) 
    227     lastupdateby = models.ForeignKey( 
    228         User, 
    229         editable = False, 
    230         null=True, blank=True 
    231     ) 
    232  
    233     def html_content(self, context): 
    234         """ 
    235         returns the generatet html code from the content applyed the markup. 
    236         """ 
    237         return apply_markup( 
    238             content = self.content, 
    239             context = context, 
    240             markup_no = self.markup 
    241         ) 
    242  
    243     def get_tag_string(self): 
    244         """ 
    245         Returns all tags as a joined string 
    246         """ 
    247         tags = self.tags.all() 
    248         tags_names = [i.name for i in tags] 
    249         return " ".join(tags_names) 
    250  
    251     def __unicode__(self): 
    252         return self.headline 
    253  
    254     class Admin: 
    255         pass 
    256  
    257     class Meta: 
    258         app_label = 'PyLucidPlugins' 
    259         ordering = ('-createtime', '-lastupdatetime') 
    260  
    261 ''' 
    262 class RssBlogEntryFeed(Feed): 
    263     """ 
    264     TODO! 
    265     http://www.djangoproject.com/documentation/syndication_feeds/ 
    266     """ 
    267     title = "Blog entry feed" 
    268     link = "/TODO/" 
    269     description = "FIXME" 
    270  
    271     def items(self): 
    272         return BlogEntry.objects.filter(is_public=True).all()[:count] 
    273  
    274 class AtomBlogEntryFeed(RssBlogEntryFeed): 
    275     feed_type = Atom1Feed 
    276     subtitle = RssBlogEntryFeed.description 
    277  
    278 class BlogEntryForm(forms.ModelForm): 
    279     """ 
    280     Form for create/edit a blog entry. 
    281     """ 
    282     content = forms.CharField( 
    283         widget=forms.Textarea(attrs={'rows': '15'}), 
    284     ) 
    285  
    286     tags = forms.CharField( 
    287         max_length=255, required=False, 
    288         help_text=_("Tags for this entry (separated by spaces.)"), 
    289         widget=forms.TextInput(attrs={'class':'bigger'}), 
    290     ) 
    291     class Meta: 
    292         model = BlogEntry 
    293 ''' 
    294  
    295 #______________________________________________________________________________ 
    296  
    297  
    298 PLUGIN_MODELS = (BlogComment, BlogTag, BlogEntry,) 
    299  
    300  
    30198 
    30299 
     
    319116        # Change the page title. 
    320117        self.current_page.title = self.preferences["blog_title"] 
     118 
     119        # The absolute url to the page witch contains the blog 
     120        self.index_url = "FIXME" 
     121 
     122        self.feed_url_prefix = self.URLs.methodLink("feed") 
    321123 
    322124 
     
    372174            context["create_url"] = self.URLs.methodLink("add_entry") 
    373175 
    374         self._render_template("display_blog", context)#, debug=2) 
     176        # Add all available syndication feeds information 
     177        context["feed_info"] = self._get_feeds_info() 
     178 
     179        self._render_template("display_blog", context, debug=0) 
    375180 
    376181    def _get_max_count(self): 
     
    405210                ) 
    406211 
    407         max = self._get_max_count() 
    408         entries = entries.all()[:max] 
     212        limit = self._get_max_count() 
     213        entries = entries.all()[:limit] 
    409214 
    410215        self._list_entries(entries) 
     
    425230            # in users. 
    426231            if self.request.user.is_anonymous(): 
    427                 msg = "Wrong url." 
    428                 if self.request.debug: 
    429                     msg += " Blog entry is not public" 
    430                 self.page_msg.red(msg) 
    431                 return 
     232                return self.error(_("Wrong URL."), "Blog entry is not public") 
    432233 
    433234        if self.request.method == 'POST': 
     
    478279            entries = entries.filter(is_public = True) 
    479280 
    480         max = self._get_max_count() 
    481         entries = entries.all()[:max] 
     281        limit = self._get_max_count() 
     282        entries = entries.all()[:limit] 
    482283 
    483284        context = { 
     
    561362            return model.objects.get(id = entry_id) 
    562363        except Exception, err: 
    563             msg = "Wrong url" 
    564             if self.request.debug: 
    565                 msg += " %s" % err 
    566             self.page_msg.red(msg) 
    567             return 
     364            return self.error(_("Wrong URL."), err) 
    568365 
    569366    def delete(self, urlargs): 
     
    893690 
    894691        self._render_template("mod_comments", context)#, debug=2) 
    895 ''' 
    896     def get_feeds_info(self): 
    897         # return the existing feed names 
    898         return (ENTRIES_FEED, COMMENTS_FEED) 
    899  
    900     def feed(self, feed_name, FeedGenerator, count=10): 
    901         """ 
    902         Feeds 
    903         * RSS 2.0 / Atom for all entries 
    904         * RSS 2.0 / Atom for the comments 
    905  
    906         FeedGenerator = django.utils.feedgenerator.Atom1Feed 
    907         or 
    908         FeedGenerator = django.utils.feedgenerator.Rss201rev2Feed 
    909  
    910  
    911         RSSfeedGenerator.lucidTag 
    912             - Generates a list of all available feeds 
    913             - The links are always /feed/PluginName/FeedName/FeedType.xml 
    914  
    915         /_command/1/RSSfeedGenerator/ 
    916  
    917  
     692 
     693    def _get_feeds_info(self): 
     694        """ 
     695        returns information about all available syndication feeds. 
     696        """ 
     697        feed_info = FeedInfo() 
     698 
     699        # Add "normal" feeds 
     700        feed_info.add( 
     701            title_info = ENTRIES_FEED_NAME, 
     702            url_parts = (self.feed_url_prefix, ENTRIES_FEED_NAME,) 
     703        ) 
     704        feed_info.add( 
     705            title_info = COMMENTS_FEED_NAME, 
     706            url_parts = (self.feed_url_prefix, COMMENTS_FEED_NAME,) 
     707        ) 
     708 
     709        # Build a list of tag feeds 
     710        limit = self.preferences.get("max_tag_feed", 10) 
     711        tags = BlogTag.objects.values_list("slug", "name").all()[:limit] 
     712        tag_feeds = [TAG_FEED_PREFIX + i[0] for i in tags] 
     713        #self.page_msg(tag_feeds) 
     714 
     715        # Add tag feeds 
     716        for tag_slug, tag_name in tags: 
     717            filename = TAG_FEED_PREFIX + tag_slug 
     718 
     719            feed_info.add( 
     720                title_info = tag_name, 
     721                url_parts = (self.feed_url_prefix, filename,) 
     722            ) 
     723 
     724        return feed_info 
     725 
     726 
     727    def feed(self, raw_feed_name): 
     728        """ 
     729        Generate and return a syndication feeds. 
     730 
     731        feed_name e.g.: 
     732            tag_%s.rss 
     733            tag_%s.atom 
     734            entries .rss/.atom 
     735            comments .rss/.atom 
    918736        """ 
    919737        title = self.preferences["blog_title"] 
    920738 
    921         if feed_name == ENTRIES_FEED: 
    922             model = BlogEntry 
     739        try: 
     740            feed_name, feed_type = os.path.splitext(raw_feed_name) 
     741            feed_type = feed_type.lstrip(".") 
     742        except Exception, err: 
     743            return self.error(_("Wrong URL."), err) 
     744 
     745        if feed_type == RSS: 
     746            FeedGenerator = feedgenerator.Rss201rev2Feed 
     747        elif feed_type == ATOM: 
     748            FeedGenerator = feedgenerator.Atom1Feed 
     749        else: 
     750            return self.error( 
     751                _("Wrong URL."), " feed type '%s' unknown." % feed_type 
     752            ) 
     753 
     754        limit = self._get_max_count() 
     755 
     756        if feed_name == ENTRIES_FEED_NAME: 
     757            # Feed with all blog entries 
     758            entries = BlogEntry.objects 
    923759            title += " - all blog entries" 
    924760 
    925         elif feed_name == COMMENTS_FEED: 
    926             model = BlogComment 
     761        elif feed_name == COMMENTS_FEED_NAME: 
     762            # Feed with all comments 
     763            entries = BlogComment.objects 
    927764            title += " - all blog comments" 
    928765 
     766        elif feed_name.startswith(TAG_FEED_PREFIX): 
     767            # Feed with all blog entries tagged with the given tag 
     768            tag_slug = feed_name[len(TAG_FEED_PREFIX):] 
     769            #self.page_msg("Tag slug: '%s'" % tag_slug) 
     770            tag_obj = BlogTag.objects.get(slug = tag_slug) 
     771            title += " - all blog entries tagged with '%s'" % tag_obj.name 
     772            entries = tag_obj.blogentry_set 
     773 
    929774        else: 
    930             raise AttributeError("Wrong feed_name.") 
    931  
    932         items = model.objects.filter(is_public=True).all()[:10] 
    933  
    934         link = self.URLs.methodLink("feed", feed_name) 
    935  
    936         feed = self._get_feed(FeedGenerator, items, title, link) 
    937  
    938         return feed.writeString('utf8') 
    939  
    940  
    941     def _get_feed(self, FeedGenerator, items, title, link): 
     775            return self.error( 
     776                _("Wrong URL."), " feed name '%s' unknown." % feed_name 
     777            ) 
     778 
     779        # Get the items 
     780        items = entries.filter(is_public=True).all()[:limit] 
     781 
     782        feed = self._get_feed(FeedGenerator, items, title) 
     783        feed_content = feed.writeString('utf8') 
     784        content_type = "%s; charset=utf-8" % feed.mime_type 
     785 
     786        if FEED_DEBUG: 
     787            self.response.write("<h2>Debug:</h2>") 
     788            self.response.write("content type: %s" % content_type) 
     789            self.response.write("<pre>") 
     790            self.response.write(escape(feed_content)) 
     791            self.response.write("</pre>") 
     792            return 
     793 
     794        # send the feed as a file to the client 
     795        response = HttpResponse(content_type=content_type)