Search code examples
djangopython-3.xdetailview

django CBV generic DetailView redirect if object does not exist


I have a DetailView which displays userprofile if the current logged in user has a userprofile created. If the user does not have a profile created, i need an else condition. Since im new to django and python, and even newer to CBV, i cannot figure out my next step. I'm hoping there is a way in def get_object() to redirect to UserProfile. Without a userprofile present it results in Related Object DoesNotExist error. How can i write an else or except condition to redirect to ProfileView (form to create profile)

PS: UserProfile is to Create a profile, UserProfileView is to View a created profile, UserProfileUpdate is to Update an existing profile.

I prefer not to pass PK though url

I am django 2.0, python 3.6.3

specifically looking for how to redirect an except/if case from DetailView methods

Models.py

class User(AbstractUser):
    """User model."""

    username = None
    email = models.EmailField(_('email address'), unique=True)
    phone = models.CharField(max_length=128, unique=True, null=True,
                             validators=[validators.RegexValidator(
                                 r'^(?:\+?(\d{2}))?(\d{10})$',
                                 _('Enter a valid phone number. Type without space or special charecter.')
                             )])

    objects = UserManager()

    REQUIRED_FIELDS = ['first_name']
    USERNAME_FIELD = 'email'

    def __str__(self):
        return self.email


class UserProfile(models.Model):
    """User Profile"""
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    country = models.CharField(max_length=128)
    state = models.CharField(max_length=128)
    city = models.CharField(max_length=128)
    landmark = models.CharField(
        max_length=128, help_text='Enter a landmark closest to you')
    address_line_1 = models.CharField(
        max_length=128, help_text='House name/Flat No')
    address_line_2 = models.CharField(
        max_length=128, help_text='Street Name/No')
    address_line_3 = models.CharField(
        max_length=128, help_text='Locality Name')
    pincode = models.IntegerField()
    land_phone = models.CharField(max_length=128, unique=True, null=True,
                                  validators=[validators.RegexValidator(
                                      r'^(?:\+?(\d{4}))\-?(\d{7})$',
                                      _('Enter a valid phone number. Type without space. Format 0400-2012345.')
                                  )])

    def __str__(self):
        return self.user.email

Views.py

class UserProfileFormView(FormView):
    form_class = UserProfileForm
    template_name = 'userprofile.html'
    success_url = '/'

    def form_valid(self, form):
        import pdb
        pdb.set_trace()
        temp_form = form.save(commit=False)
        temp_form.user = self.request.user
        temp_form.save()
        return super().form_valid(form)

    def form_invalid(self, form):
        response = super().form_invalid(form)
        return redirect('users:userprofile')


class UserProfileView(DetailView):
    model = UserProfile
    context_object_name = 'userprofile'
    template_name = 'x.html'

    # def dispatch(self, request, *args, **kwargs):
    #     import pdb; pdb.set_trace()
    #     if self.request.user.userprofile.pk is not None:
    #         pass
    #     else:
    #         return redirect('users:userprofile')

    def get_object(self):
        import pdb; pdb.set_trace()
        self.object = UserProfile.objects.get(pk=self.request.user.userprofile.pk)
        return self.object

    # def get_context_data(self, **kwargs):
    #     import pdb; pdb.set_trace()
    #     context = kwargs
    #     context_object_name = 'userprofile'
    #     context['userprofile'] = UserProfile.objects.get(pk=self.request.user.userprofile.pk)
    #     if context_object_name:            
    #         return context
    #     else:
    #         return redirect('users:userprofile')


class UserProfileUpdate(UpdateView):
    model = UserProfile
    fields = ('address_line_1', 'address_line_2', 'address_line_3',
              'landmark', 'city', 'state', 'country', 'pincode', 'land_phone')

    template_name = 'userprofile.html'
    success_url = 'home'

Solution

  • So after quite a few searching over the internet this is the best answer i could come up with. Hope someone else will find this useful, or some one with better knowledge has a more optimal solution.

    class UserProfileView(DetailView):
        model = UserProfile
        context_object_name = 'userprofile'
        template_name = 'x.html'
    
    
        def dispatch(self, request, *args, **kwargs):
            try:
                self.object =  self.request.user.userprofile
            except:
                return redirect('users:userprofile')
    
            self.get_object()
            return super(UserProfileView, self).get(request, *args, **kwargs)
    
        def get_object(self):        
            self.object = self.request.user.userprofile
            return self.object
    

    What i did here (or rather what django does for me ) is use dispatch. By using dispatch i was able to filter only those users with profiles to proceed to get the profile or return a UserProfile object using get_object method. If a user does not have profile then the except condition in dispatch method redirects the user to profile creation page. If a user has profile then dispatch calls (overide. i am not sure which term to use) get_object method to get the existing object. Well to sum it up, get_object method can only return objects as response. So my attempts (commented section of code) in trying to redirect could not work as django wont allow it. If any of my explanation is wrong, please do point out my drawbacks