views:

275

answers:

2

So I'm trying to create a new feed within the Admin page and its crashing with the error IntegrityError: lifestream_feed.lifestream_id may not be NULL, form['lifestream'] is set but form.instance.lifestream is not.

form.fields even shows that lifestream is a django.forms.models.ModelChoiceField

Here is the code:

class FeedCreationForm(forms.ModelForm):
    class Meta:
        model = Feed
        exclude = ['name', 'domain', 'fetchable']

    def parse_feed(self, feed_url):
        feed = feedparser.parse(feed_url)

        # Does the feed have errors
        if feed['bozo']:
            if feed['feed'].has_key("links"):
                for link in feed['feed']['links']:
                    if link["type"] == "application/rss+xml":
                        feed = self.parse_feed(link['href'])
                        if not feed['bozo']:
                            return feed
        else:
            return feed

        return None

    def clean(self):
        """
        Checks to make sure a feed url is valid and gets the feed
title
        and domain.
        """
        feed_url = self.cleaned_data.get('url')
        if not feed_url:
            # Feed url was not validated by the field validator
            return
        feed = self.parse_feed(feed_url)
        if feed:
            feed_url = feed['url']
            self.cleaned_data['url'] = feed_url
        else:
            # the feed was not parsed correctly.
            import logging

            self._errors['url'] = ErrorList(["This is not a valid
feed: %s" % feed['bozo_exception']])
            logging.error(feed['bozo_exception'])
            # This field is no longer valid. Remove from cleaned_data
            del self.cleaned_data['url']
            return

        # Check if the feed has a title field
        feed_info = feed.get('feed')
        if not feed_info.get('title'):
            self._errors['url'] = ErrorList(["This is not a valid
feed: The feed is empty"])
            # This field is no longer valid. Remove from cleaned_data
            del self.cleaned_data['url']
            return
        self.cleaned_data['name'] = feed_info['title']
        self.instance.name = self.cleaned_data['name']
        self.cleaned_data['domain'] = get_url_domain(feed_url)
        self.instance.domain = self.cleaned_data['domain']

        return self.cleaned_data

class FeedAdmin(admin.ModelAdmin):
    list_display    = ('name', 'lifestream', 'domain', 'fetchable')
    list_filter     = ('domain', 'lifestream')
    actions         = ['make_fetchable', 'make_unfetchable']

    add_form = FeedCreationForm
    model = Feed

    def make_unfetchable(self, request, queryset):
        queryset.update(fetchable=False)
    make_unfetchable.short_description = _(u"Mark as unfetchable")

    def make_fetchable(self, request, queryset):
        queryset.update(fetchable=True)
    make_fetchable.short_description = _(u"Mark as fetchable")

    def add_view(self, request):
        if not self.has_change_permission(request):
            raise PermissionDenied
        if request.method == 'POST':
            form = self.add_form(request.POST)
            if form.is_valid():
                new_feed = form.save()
                msg = _('The %(name)s "%(obj)s" was added
successfully.') % {'name': 'user', 'obj': new_feed}
                self.log_addition(request, new_feed)
                if "_addanother" in request.POST:
                    request.user.message_set.create(message=msg)
                    return HttpResponseRedirect(request.path)
                elif '_popup' in request.REQUEST:
                    return self.response_add(request, new_feed)
                else:
                    request.user.message_set.create(message=msg + ' '
+ ugettext("You may edit it again below."))
                    # TODO: use reversed url
                    return HttpResponseRedirect('../%s/' %
new_feed.id)
        else:
            form = self.add_form()

        return render_to_response('admin/lifestream/feed/
add_form.html', {
            'title': _('Add feed'),
            'form': form,
            'is_popup': '_popup' in request.REQUEST,
            'add': True,
            'change': False,
            'has_add_permission': True,
            'has_delete_permission': False,
            'has_change_permission': True,
            'has_file_field': False,
            'has_absolute_url': False,
            'auto_populated_fields': (),
            'opts': self.model._meta,
            'save_as': False,
            #'username_help_text': self.model._meta.get_field
('username').help_text,
            'root_path': self.admin_site.root_path,
            'app_label': self.model._meta.app_label,
        }, context_instance=template.RequestContext(request))

    def queryset(self, request):
        return self.model.objects.feeds()

admin.site.register(Feed, FeedAdmin)

class Lifestream(models.Model):
    """
    A lifestream. Lifestreams can be created per user.
    """
    site = models.ForeignKey(Site, verbose_name=_(u"site"))
    user = models.ForeignKey(User, verbose_name=_(u"user"))
    slug = models.SlugField(_("slug"), help_text=_('Slug for use in
urls (Autopopulated from the title).'), blank=True)
    title = models.CharField(_("title"), max_length=255)

    def __unicode__(self):
        return self.title

class FeedManager(models.Manager):
    ''' Query only normal feeds. '''

    def feeds(self):
        return super(FeedManager, self).get_query_set()

    def fetchable(self):
        return self.feeds().filter(fetchable=True)

class Feed(models.Model):
    '''A feed for gathering data.'''
    lifestream = models.ForeignKey(Lifestream, verbose_name=_
('lifestream'))
    name = models.CharField(_("feed name"), max_length=255)
    url = models.URLField(_("feed url"), help_text=_("Must be a valid
url."), verify_exists=True, max_length=1000)
    domain = models.CharField(_("feed domain"), max_length=255)
    fetchable = models.BooleanField(_("fetchable"), default=True)

    # The feed plugin name used to process the incoming feed data.
    plugin_class_name = models.CharField(_("plugin name"),
max_length=255, null=True, blank=True, choices=getattr(settings,
"PLUGINS", PLUGINS))

    objects = FeedManager()

    def __unicode__(self):
        return self.name 

Its not returning the empty returns, its reaching the return self.cleaned_data:

-> return self.cleaned_data
(Pdb) list
 85             self.cleaned_data['name'] = feed_info['title']
 86             self.instance.name = self.cleaned_data['name']
 87             self.cleaned_data['domain'] = get_url_domain(feed_url)
 88             self.instance.domain = self.cleaned_data['domain']
 89     
 90  ->         return self.cleaned_data
 91     
 92     class FeedAdmin(admin.ModelAdmin):
 93         list_display    = ('name', 'lifestream', 'domain', 'fetchable')
 94         list_filter     = ('domain', 'lifestream')
 95         actions         = ['make_fetchable', 'make_unfetchable']
(Pdb) self.cleaned_data
{'url': u'http://twitter.com/statuses/user_timeline/6166742.rss', 'domain': u'twitter.com', 'lifestream': <Lifestream: Social>, 'name': u'Twitter / sontek', 'plugin_class_name': u'lifestream.plugins.twitter.TwitterPlugin'}
A: 

I believe the problem is in your clean() method.

The general clean() method (as opposed to field specific clean methods like clean_domain()) must return the cleaned_data dictionary (minus any fields which do not validate), and you have at least 3 returns in your clean method that do not return anything.

See here

Van Gale
Those returns are when the cleaning fails, the error I'm getting is hitting the final return of the cleaned data, there are no errors.
sontek
(Pdb) list 85 self.cleaned_data['name'] = feed_info['title'] 86 self.instance.name = self.cleaned_data['name'] 87 self.cleaned_data['domain'] = get_url_domain(feed_url) 88 self.instance.domain = self.cleaned_data['domain'] 89 90 -> return self.cleaned_data 91 92 class FeedAdmin(admin.ModelAdmin): 93 list_display = ('name', 'lifestream', 'domain', 'fetchable') 94 list_filter = ('domain', 'lifestream') 95 actions = ['make_fetchable', 'make_unfetchable']
sontek
(Pdb) self.cleaned_data{'url': u'http://twitter.com/statuses/user_timeline/6166742.rss', 'domain': u'twitter.com', 'lifestream': <Lifestream: Social>, 'name': u'Twitter / sontek', 'plugin_class_name': u'lifestream.plugins.twitter.TwitterPlugin'}
sontek
A: 

Turns out this is a bug in HEAD of django

sontek
link to the bug report/status?
Shane
They are still discussing what should be done, the error is caused by not calling super of the clean method... which should be done anyways, but its a backwards compatibility issue since you didn't have to do it before.
sontek
A link to the ticket would have been a huge help. I ran into what I suspect is the same issue. Changing fromreturn self.cleaned_datatoreturn super(MyCustomAdminForm, self).clean()fixed the issue
Tom
Yeah, sorry... The issue is they now require you to call super when before you didn't.
sontek