Skip to content
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

Custom User Model Breaking Profile Functionality #34

Open
zetas opened this issue Feb 13, 2014 · 11 comments
Open

Custom User Model Breaking Profile Functionality #34

zetas opened this issue Feb 13, 2014 · 11 comments

Comments

@zetas
Copy link

zetas commented Feb 13, 2014

With Django 1.5 the profile functionality was deprecated in favor of the new custom user model implementation. In my app, I'm using a custom user model for authentication. Currently I'm using Django 1.6.2

When trying to run the example code for stripe:

class UpgradeView(View):
    form_class = StripePaymentForm
    template = 'account/checkout.html'

    def get(self, request, *args, **kwargs):
        return render(request, self.template, {'form': self.form_class()})

    def post(self, request, *args, **kwargs):
        zebra_form = StripePaymentForm(request.POST)
        if zebra_form.is_valid():
            my_profile = request.user.get_profile()
            stripe_customer = stripe.Customer.retrieve(my_profile.stripe_customer_id)
            stripe_customer.card = zebra_form.cleaned_data['stripe_token']
            stripe_customer.save()

            my_profile.last_4_digits = zebra_form.cleaned_data['last_4_digits']
            my_profile.stripe_customer_id = stripe_customer.id
            my_profile.save()
        return render(request, self.template, {'form': zebra_form})

I get an exception saying my custom user model doesn't have the method "get_profile()". Do I need to add some mixins to my user class or just use a profile model anyway?

Here's a copy of my user model and it's manager just in case, note the inclusion of data that would normally be in the profile:

class WUserManager(BaseUserManager):
    def create_user(self, email, password=None):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=self.normalize_email(email),
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password):
        """
        Creates and saves a superuser with the given email and password.
        """
        user = self.create_user(email,
            password=password,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user


class WUser(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
        db_index=True,
    )

    first_name = models.CharField(max_length=100, null=True, blank=True)
    last_name = models.CharField(max_length=150, null=True, blank=True)
    address = models.CharField(max_length=200, null=True, blank=True)
    city = models.CharField(max_length=100, null=True, blank=True)
    state = models.CharField(max_length=50, null=True, blank=True)
    zip = models.PositiveIntegerField(max_length=5, null=True, blank=True)
    account_type = models.CharField(max_length=20)
    created = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = WUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def get_full_name(self):
        # The user is identified by their email address
        if self.first_name and self.last_name:
            return self.first_name + ' ' + self.last_name

        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        if self.first_name:
            return self.first_name

        return self.email.split('@')[0]

    def get_license_status(self):
        try:
            license = self.license
        except License.DoesNotExist:
            license = None

        if license is not None and isinstance(license, License):
            return True

        return False

    # On Python 3: def __str__(self):
    def __unicode__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):        
        return True

    @property
    def is_staff(self):
        return self.is_admin

    class Meta:
        verbose_name = 'User'
        verbose_name_plural = 'Users'
@leetrout
Copy link
Contributor

I'm 90% sure that method would have to be added since you're using a custom model. I'll double check later today.

@zetas
Copy link
Author

zetas commented Feb 13, 2014

Thanks, I really appreciate you getting back to me, i have a deadline to get this billing module completed by this weekend and I'm running into a brick wall. Already spent 4 days with django-stripe-payments just to toss it for lack of docs and move onto this.

@leetrout
Copy link
Contributor

https://docs.djangoproject.com/en/1.5/ref/settings/#std:setting-AUTH_PROFILE_MODULE

Looks like you can just toss it on there- django isn't going to use it internally.

Try something like:

def get_profile(self):
    return self

and see if that works...

@zetas
Copy link
Author

zetas commented Feb 13, 2014

Ok, i added that method to the WUser class and after submitting the charge form again I get this error:

AttributeError at /account/upgrade/
'WUser' object has no attribute 'stripe_customer_id'

@zetas
Copy link
Author

zetas commented Feb 13, 2014

Should I just add the attributes as they error out or use a mixin? I'm still a new django dev so I'm a little shaky on the best practice here.

Thanks again for your time.

@leetrout
Copy link
Contributor

Try class WUser(AbstractBaseUser, StripeMixin) where StripeMixin is from zebra.mixins import StripeMixin.

You might also try class WUser(AbstractBaseUser, ZebraMixin) which is the "kitchen sink".

@leetrout
Copy link
Contributor

And look through https://github.com/GoodCloud/django-zebra/blob/master/zebra/mixins.py

I don't know why our autodocs didn't pick up all the docstrings but they're there.

@zetas
Copy link
Author

zetas commented Feb 13, 2014

Ok, I tried both mixins, nothing changed, still complaining about a lack of stripe_customer_id. The StripeMixin says it provides a stripe attribute but it doesn't provide stripe_customer_id which seems to be needed at the profile level.

I went ahead and added it with the following settings to the WUser class:

class WUser(AbstractBaseUser, StripeMixin):
    #...
    stripe_customer_id = models.PositiveIntegerField(null=True, blank=True)

Now I get this error when trying to post the credit card form:

InvalidRequestError at /account/upgrade/
Could not determine which URL to request: Customer instance has invalid ID: None

@leetrout
Copy link
Contributor

My bad- That should be WUser(AbstractBaseUser, StripeCustomer) using from zebra.models import StripeCustomer

https://github.com/GoodCloud/django-zebra/blob/master/zebra/models.py

@zetas
Copy link
Author

zetas commented Feb 13, 2014

I removed the stripe_customer_id i put in the WUser class and changed to subclass StripeCustomer as you suggest. I still get the same error as before, the "Customer instance has invalid ID: None".

I feel like I'm doing something wrong, maybe I didn't set it up right. Also, this is my first attempt to get Zebra working with a brand new billing system, here's the template contents just in case it helps:

{% extends 'base.html' %}
{% load zebra_tags %}

{% block static_content %}
    {{ block.super }}
    {% zebra_head_and_stripe_key %}
{% endblock %}

{% block content %}
    <div class="body_container_info">
        <div class="body">
                    {% zebra_card_form %}
                <div id="checkout_confirm">
                </div>
            </div>
        </div>
    </div>
{% endblock %}

@zetas
Copy link
Author

zetas commented Feb 14, 2014

As I've been digging into this, I started looking at the Stripe Python Library and it looks like it may be easier to implement the library by itself rather than using an abstraction layer like zebra. The two I've tried so far have been extremely uncooperative in my setup which tells me either they are pretty fragile libraries or I'm using some weird edge case environment, what with the custom user model.

I'll leave this issue open for a bit to see if anyone has any thoughts as to how to get it working with my environment, if it's even possible; just to provide an answer to others that may be having or have this issue in the future.

I want to personally thank @leetrout for helping me work through this 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants