profile
viewpoint
Patryk Zawadzki patrys Mirumee Software Wrocław, Poland Head of Technology at Mirumee Software

mirumee/macaw-ui 10

MacawUI: an official UI design kit for Saleor

patrys/django-channels-ariadne 6

An async experiment with Ariadne + Django Channels

patrys/daemontools-ng 4

A modern, compatible, LGPL alternative to daemontools

mirumee/opentracing-asgi 3

ASGI middleware reporting to the OpenTracing API

patrys/django-friends 3

friendship, contact and invitation management for the Django web framework

patrys/babel 2

A GitHub mirror of http://svn.edgewall.org/repos/babel/

patrys/blipy 2

A Python API for blip.pl

AndrewIngram/django-treebeard 1

django-treebeard, modified to support multi-db.

patrys/django-explicit 1

Explicit querysets for Django

fork emesik/hosted-monero-api-spec

The open specification of the hosted Monero "light" wallet server API standard

fork in 2 days

issue commentmirumee/ariadne

mutation using a list of objects in from query returns a non-nullable field error

Hi @ccsv

Your resolver's return value is invalid. Your schema tells GraphQL query executor that you will return a list, but you return {'document': doc}. This means query executor iterates on dict's keys, and those will be strings that don't have name attr.

ccsv

comment created time in 3 days

issue closedmirumee/ariadne

mutation using a list of objects in from query returns a non-nullable field error

Not sure if I am doing something wrong but I am getting an non-nullable field error when trying to make a bulk create with Django and Ariadne.

I have a model Document that just has two fields id and name. I am doing a bulk create mutation where I insert a list of dicts and return a list of objects that were created. What I expect is a return of a list of objects that is looped through by the graphql terminal to print out the objects created with the fields that are entered.

Here is the schema I am using:

type_defs = gql("""
   type Document {
        id:ID
        name: String!
   }
    input DocumentInput {
        id:ID
        name:String
    }

   type Mutation{
       CreateDocument(input:[DocumentInput]):[Document]
    }
    """)

   mutation = MutationType()

@mutation.field("CreateDocument")
def CreateDocument(*_, input):
    doc = [Document.objects.create(name=inputs["name"]) for inputs in input]
    return {'document': doc}

The error I get is:

 "errors": [
    {
      "message": "Cannot return null for non-nullable field Document.name.",
      "locations": [
        {
          "line": 4,
          "column": 5
        }
      ],

If I print the variable doc in the resolver it returns a list of objects eg. [<Document: Foo>, <Document: Bar>] The context section from the terminal:

          "context": {
            "self": "<graphql.exec...00216F8EED8E0>",
            "return_type": "<GraphQLNonNu...ype 'String'>>",
            "field_nodes": "[FieldNode at 86:90]",
            "info": "GraphQLResolv...'/graphql/'>})",
            "path": "Path(prev=Pat...), key='name')",
            "result": "None",
            "completed": "None"
          }

Not sure if it is a bug or I wrote something wrong.

closed time in 3 days

ccsv

issue openedmirumee/ariadne

mutation using a list of objects in from query returns a non-nullable field error

Not sure if I am doing something wrong but I am getting an non-nullable field error when trying to make a bulk create with Django and Ariadne.

I have a model Document that just has two fields id and name. I am doing a bulk create mutation where I insert a list of dicts and return a list of objects that were created. What I expect is a return of a list of objects that is looped through by the graphql terminal to print out the objects created with the fields that are entered.

Here is the schema I am using:

type_defs = gql("""
   type Document {
        id:ID
        name: String!
   }
    input DocumentInput {
        id:ID
        name:String
    }

   type Mutation{
       CreateDocument(input:[DocumentInput]):[Document]
    }
    """)

   mutation = MutationType()

@mutation.field("CreateDocument")
def CreateDocument(*_, input):
    doc = [Document.objects.create(name=inputs["name"]) for inputs in input]
    return {'document': doc}

The error I get is:

 "errors": [
    {
      "message": "Cannot return null for non-nullable field Document.name.",
      "locations": [
        {
          "line": 4,
          "column": 5
        }
      ],

If I print the variable doc in the resolver it returns a list of objects eg. [<Document: Foo>, <Document: Bar>] The context section from the terminal:

          "context": {
            "self": "<graphql.exec...00216F8EED8E0>",
            "return_type": "<GraphQLNonNu...ype 'String'>>",
            "field_nodes": "[FieldNode at 86:90]",
            "info": "GraphQLResolv...'/graphql/'>})",
            "path": "Path(prev=Pat...), key='name')",
            "result": "None",
            "completed": "None"
          }

Not sure if it is a bug or I wrote something wrong.

created time in 4 days

issue commentmirumee/ariadne

Support OPTIONS requests

@seimsel do you have example for graphql_sync? how to use cors in that case?

No. I never used this.

chriswingler

comment created time in 5 days

issue commentmirumee/ariadne

Support OPTIONS requests

@seimsel do you have example for graphql_sync? how to use cors in that case?

chriswingler

comment created time in 6 days

fork glensc/github-action-next-semvers

Github Action that output the next version for major, minor, and patch version based on the given semver version.

fork in 6 days

fork glensc/github-actions

Monorepo for all Symplify the Github Actions

fork in 6 days

pull request commentmirumee/ariadne

Bump opentracing from 2.3.0 to 2.4.0

Codecov Report

Merging #447 (9851eaa) into master (72b0e12) will decrease coverage by 0.04%. The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #447      +/-   ##
==========================================
- Coverage   98.17%   98.13%   -0.05%     
==========================================
  Files         104      104              
  Lines        5093     4978     -115     
==========================================
- Hits         5000     4885     -115     
  Misses         93       93              
Impacted Files Coverage Δ
tests/conftest.py 94.54% <0.00%> (-0.54%) :arrow_down:
tests/wsgi/conftest.py 97.61% <0.00%> (-0.46%) :arrow_down:
tests/federation/test_interfaces.py 94.00% <0.00%> (-0.12%) :arrow_down:
ariadne/file_uploads.py 98.18% <0.00%> (-0.10%) :arrow_down:
tests/test_directives.py 97.56% <0.00%> (-0.02%) :arrow_down:
tests/test_enums.py 100.00% <0.00%> (ø)
tests/test_unions.py 100.00% <0.00%> (ø)
ariadne/extensions.py 100.00% <0.00%> (ø)
tests/test_graphql.py 100.00% <0.00%> (ø)
tests/test_objects.py 100.00% <0.00%> (ø)
... and 24 more

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update afa9fad...9851eaa. Read the comment docs.

dependabot-preview[bot]

comment created time in 7 days

pull request commentmirumee/ariadne

Bump pip-tools from 5.3.1 to 5.4.0

Codecov Report

Merging #446 (1fa0466) into master (72b0e12) will decrease coverage by 0.04%. The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #446      +/-   ##
==========================================
- Coverage   98.17%   98.13%   -0.05%     
==========================================
  Files         104      104              
  Lines        5093     4978     -115     
==========================================
- Hits         5000     4885     -115     
  Misses         93       93              
Impacted Files Coverage Δ
tests/conftest.py 94.54% <0.00%> (-0.54%) :arrow_down:
tests/wsgi/conftest.py 97.61% <0.00%> (-0.46%) :arrow_down:
tests/federation/test_interfaces.py 94.00% <0.00%> (-0.12%) :arrow_down:
ariadne/file_uploads.py 98.18% <0.00%> (-0.10%) :arrow_down:
tests/test_directives.py 97.56% <0.00%> (-0.02%) :arrow_down:
tests/test_enums.py 100.00% <0.00%> (ø)
tests/test_unions.py 100.00% <0.00%> (ø)
ariadne/extensions.py 100.00% <0.00%> (ø)
tests/test_graphql.py 100.00% <0.00%> (ø)
tests/test_objects.py 100.00% <0.00%> (ø)
... and 24 more

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update afa9fad...1fa0466. Read the comment docs.

dependabot-preview[bot]

comment created time in 7 days

create barnchmirumee/ariadne

branch : dependabot/pip/opentracing-2.4.0

created branch time in 7 days

PR opened mirumee/ariadne

Bump opentracing from 2.3.0 to 2.4.0

Bumps opentracing from 2.3.0 to 2.4.0. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/opentracing/opentracing-python/blob/master/CHANGELOG.rst">opentracing's changelog</a>.</em></p> <blockquote> <h1>2.4.0 (2020-11-19)</h1> <ul> <li>Use current_task from asyncio module for Python 3.9 compatibility (<a href="https://github-redirect.dependabot.com/opentracing/opentracing-python/issues/138">#138</a>) <Michael Tannenbaum></li> <li>Drop build support for Python 3.5 (<a href="https://github-redirect.dependabot.com/opentracing/opentracing-python/issues/138">#138</a>) <Michael Tannenbaum></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/opentracing/opentracing-python/commit/98e106e793dbea922b72c214d300e2e6a1556134"><code>98e106e</code></a> Preparing release 2.4.0</li> <li><a href="https://github.com/opentracing/opentracing-python/commit/fddddbaa6a0d0c57c6ad666ecfc1323dc315aebd"><code>fddddba</code></a> Use current_task from asyncio module for Python 3.9 compatibility (<a href="https://github-redirect.dependabot.com/opentracing/opentracing-python/issues/138">#138</a>)</li> <li><a href="https://github.com/opentracing/opentracing-python/commit/aef28a7acef98589a0e630c551488803e0ef3f42"><code>aef28a7</code></a> Back to development: 2.3.1</li> <li>See full diff in <a href="https://github.com/opentracing/opentracing-python/compare/2.3.0...2.4.0">compare view</a></li> </ul> </details> <br />

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


<details> <summary>Dependabot commands and options</summary> <br />

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
  • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
  • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
  • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language
  • @dependabot badge me will comment on this PR with code to add a "Dependabot enabled" badge to your readme

Additionally, you can set the following in your Dependabot dashboard:

  • Update frequency (including time of day and day of week)
  • Pull request limits (per update run and/or open at any time)
  • Out-of-range updates (receive only lockfile updates, if desired)
  • Security updates (receive only security updates, if desired)

</details>

+1 -1

0 comment

1 changed file

pr created time in 7 days

create barnchmirumee/ariadne

branch : dependabot/pip/pip-tools-5.4.0

created branch time in 7 days

PR opened mirumee/ariadne

Bump pip-tools from 5.3.1 to 5.4.0

Bumps pip-tools from 5.3.1 to 5.4.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/jazzband/pip-tools/releases">pip-tools's releases</a>.</em></p> <blockquote> <h2>5.4.0</h2> <p>Features:</p> <ul> <li>Add <code>pip>=20.3</code> support (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/pull/1216">1216</a>). Thanks <a href="https://github.com/atugushev">@atugushev</a> and <a href="https://github.com/AndydeCleyre">@AndydeCleyre</a></li> <li>Exclude <code>--no-reuse-hashes</code> option from «command to run» header (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/pull/1197">1197</a>). Thanks <a href="https://github.com/graingert">@graingert</a></li> </ul> <p>Dependencies:</p> <ul> <li>Bump <code>pip</code> minimum version to <code>>= 20.1</code> (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/pull/1191">1191</a>). Thanks <a href="https://github.com/atugushev">@atugushev</a> and <a href="https://github.com/AndydeCleyre">@AndydeCleyre</a></li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/jazzband/pip-tools/blob/master/CHANGELOG.md">pip-tools's changelog</a>.</em></p> <blockquote> <h1>5.4.0 (2020-11-21)</h1> <p>Features:</p> <ul> <li>Add <code>pip>=20.3</code> support (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/pull/1216">1216</a>). Thanks <a href="https://github.com/atugushev">@atugushev</a> and <a href="https://github.com/AndydeCleyre">@AndydeCleyre</a></li> <li>Exclude <code>--no-reuse-hashes</code> option from «command to run» header (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/pull/1197">1197</a>). Thanks <a href="https://github.com/graingert">@graingert</a></li> </ul> <p>Dependencies:</p> <ul> <li>Bump <code>pip</code> minimum version to <code>>= 20.1</code> (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/pull/1191">1191</a>). Thanks <a href="https://github.com/atugushev">@atugushev</a> and <a href="https://github.com/AndydeCleyre">@AndydeCleyre</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/jazzband/pip-tools/commit/7521ec8402823c52fdc7d3827ff139466069ce33"><code>7521ec8</code></a> 5.4.0 changelog</li> <li><a href="https://github.com/jazzband/pip-tools/commit/55ceda567c3255c4844ff5468ceaf9137a597cf0"><code>55ceda5</code></a> Add pip>=20.3 support (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/issues/1216">#1216</a>)</li> <li><a href="https://github.com/jazzband/pip-tools/commit/05c9ec00b80f2d59e925b8e3475833e677c9c5a6"><code>05c9ec0</code></a> Replace Travis CI with GitHub Actions (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/issues/1229">#1229</a>)</li> <li><a href="https://github.com/jazzband/pip-tools/commit/61eb515b430a57e71f0bf768049e6a1f23d3c7c8"><code>61eb515</code></a> Add --no-reuse-hashes to COMPILE_EXCLUDE_OPTIONS (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/issues/1197">#1197</a>)</li> <li><a href="https://github.com/jazzband/pip-tools/commit/e8af4f43ec608780de0bc641fb282d6643f26f97"><code>e8af4f4</code></a> Simplify isort configuration using 'profile = black' (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/issues/1204">#1204</a>)</li> <li><a href="https://github.com/jazzband/pip-tools/commit/55f1d87382ad66cea35d11088236405167fe35d4"><code>55f1d87</code></a> Drop pip-20.0 support (<a href="https://github-redirect.dependabot.com/jazzband/pip-tools/issues/1191">#1191</a>)</li> <li>See full diff in <a href="https://github.com/jazzband/pip-tools/compare/5.3.1...5.4.0">compare view</a></li> </ul> </details> <br />

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


<details> <summary>Dependabot commands and options</summary> <br />

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
  • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
  • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
  • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language
  • @dependabot badge me will comment on this PR with code to add a "Dependabot enabled" badge to your readme

Additionally, you can set the following in your Dependabot dashboard:

  • Update frequency (including time of day and day of week)
  • Pull request limits (per update run and/or open at any time)
  • Out-of-range updates (receive only lockfile updates, if desired)
  • Security updates (receive only security updates, if desired)

</details>

+1 -1

0 comment

1 changed file

pr created time in 7 days

issue commentmirumee/ariadne

`make_executable_schema`'s omits default enum values in args and inputs

PR's welcome :)

In the meantime workaround for this issue is to define default value in Python, eg:

def resolve(_, info, *, role=Roles.ADMIN)
rafalp

comment created time in 9 days

issue commentmirumee/ariadne

`make_executable_schema`'s omits default enum values in args and inputs

Any update on this? The issue was created quite a while ago and feels like a pretty common use case. I've only been playing with ariadne for a couple days and hit the bug immediately.

rafalp

comment created time in 9 days

fork glensc/flysystem-sftp

Flysystem Adapter for SFTP

fork in 9 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):+        """ Get user email """+        try:+            # Most common implementation+            return self.get_user().email+        except AttributeError:+            raise NotImplementedError()++    def get_user_first_name(self):+        """+        Get user first name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().first_name+        except AttributeError:+            raise NotImplementedError()++    def get_user_last_name(self):+        """+        Get user last name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().last_name+        except AttributeError:+            raise NotImplementedError()++    def get_renew_token(self):

Probably the concrete Payment class created by users of django-plans would have to include a field specific for it. You can also provide a mixin, so that implementors do something like

class Payment(BasePayment, SubscriptionMixin):
    # SubscriptionMixin has a foreign key to the concrete subscription model.

I do have to point out that card_expire_year, card_expire_month and card_masked_number all make the assumption that the payment method was a credit card, which is not always the case.

PetrDlouhy

comment created time in 11 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):+        """ Get user email """+        try:+            # Most common implementation+            return self.get_user().email+        except AttributeError:+            raise NotImplementedError()++    def get_user_first_name(self):+        """+        Get user first name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().first_name+        except AttributeError:+            raise NotImplementedError()++    def get_user_last_name(self):+        """+        Get user last name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().last_name+        except AttributeError:+            raise NotImplementedError()++    def get_renew_token(self):

The Subscription model is similar to what I am storing the parameters of this method to: https://github.com/django-getpaid/django-plans/blob/master/plans/models.py#L402

So the abstraction could look something like this:

class BaseSubscription(models.Model):
    user_plan = models.OneToOneField('UserPlan', on_delete=models.CASCADE, related_name='recurring')
    token = models.CharField(
        _('recurring token'),
        help_text=_('Token, that will be used for payment renewal. Depends on used payment provider'),
        max_length=255,
        default=None,
        null=True,
        blank=True,
    )
    card_expire_year = models.IntegerField(null=True, blank=True)
    card_expire_month = models.IntegerField(null=True, blank=True)
    card_masked_number = models.CharField(null=True, blank=True, max_length=255)
        
    class Meta: 
        abstract = True

But I am not sure, how to implement this to django-payments without implying too much about the usage logic e.g. how to initialise such objects inside Payment model.

PetrDlouhy

comment created time in 11 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):

Oh, this is something, I got totally wrong. Sorry for loss of your time and thank you for clearing that out. I used this for storing issuer info when I was implementing PayPal (which doesn't use it at all), and then continued with the wrong implementation for PayU and recurring payments.

Maybe it would be useful to put some info about those fields into docs, I will try to write something.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):+        """ Get user email """+        try:+            # Most common implementation+            return self.get_user().email+        except AttributeError:+            raise NotImplementedError()++    def get_user_first_name(self):+        """+        Get user first name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().first_name+        except AttributeError:+            raise NotImplementedError()++    def get_user_last_name(self):+        """+        Get user last name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().last_name+        except AttributeError:+            raise NotImplementedError()++    def get_renew_token(self):

An alternative would also be for you to create an explicit Subscription object, and save the data there.

You can then link payments to subscriptions. I think this also slightly mirrors how payment providers themselves model these things.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):+        """ Get user email """+        try:+            # Most common implementation+            return self.get_user().email+        except AttributeError:+            raise NotImplementedError()++    def get_user_first_name(self):+        """+        Get user first name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().first_name+        except AttributeError:+            raise NotImplementedError()++    def get_user_last_name(self):+        """+        Get user last name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().last_name+        except AttributeError:+            raise NotImplementedError()++    def get_renew_token(self):

You can use payment.attrs to store custom per-provider data. I'd suggest using that.

For now it's a proxy that JSON-encodes into a string field (legacy reasons). I'm planning to migrate this into a plain JSONField, but usage should remain pretty much the same.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):

The billing_ fields all hold billing details, that is: the details of the person paying (this is what payment providers usually ask for, and the thing that actually varies on a per-payment basis).

This is the assumption made for other payment implementations, so it's the assumption that any third-party providers should make two.

Using billing_ fields as something elsemeans that applications cannot use PayU and another provider, since the give different semantic meaning to the same fields.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):

You're assuming that all applications have a User for each payment made.

I'm not a fan of imposing such a big restriction, especially without the need for one. You probably want a user in your own Payment instance, not in the abstract one in this package.

PetrDlouhy

comment created time in 12 days

pull request commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

Here is sample implementation of the methods that connects django-payments to django-plans: https://github.com/PetrDlouhy/django-plans-payments/blob/feature/recurring-payments/plans_payments/models.py

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):

This is simple helper method used by the other get_user_* methods, so it is the only method, that needs to be overridden in case of the Django django.contrib.auth.models.User model.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):+        """ Get user email """+        try:+            # Most common implementation+            return self.get_user().email+        except AttributeError:+            raise NotImplementedError()++    def get_user_first_name(self):+        """+        Get user first name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().first_name+        except AttributeError:+            raise NotImplementedError()++    def get_user_last_name(self):+        """+        Get user last name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().last_name+        except AttributeError:+            raise NotImplementedError()++    def get_renew_token(self):

To make recurring payments work, there needs to be token associate with the user. I wanted to provide standardized way to store textual tokens for usage across django-payments payment providers.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):

The billing_* fields hold information about the issuer (Me), PayU (and potentially other providers) needs information about the user.

PetrDlouhy

comment created time in 12 days

Pull request review commentjazzband/django-payments

Payment model changes for recurring payments and PayU backend

 def get_success_url(self) -> str:     def get_process_url(self) -> str:         return reverse('process_payment', kwargs={'token': self.token}) +    def get_payment_url(self):+        """+        Get the url the view that handles the payment (payment_details() in documentation)+        For now used only by PayU provider to redirect users back to CVV2 form+        """+        raise NotImplementedError()++    def get_user(self):+        """ Get the user asociated with this payment """+        raise NotImplementedError()++    def get_user_email(self):+        """ Get user email """+        try:+            # Most common implementation+            return self.get_user().email+        except AttributeError:+            raise NotImplementedError()++    def get_user_first_name(self):+        """+        Get user first name for purposes of the payment provider+        Used only by PayU provider for now+        """+        try:+            # Most common implementation+            return self.get_user().first_name+        except AttributeError:+            raise NotImplementedError()++    def get_user_last_name(self):

Ditto billing_last_name.

PetrDlouhy

comment created time in 12 days

more