Re: Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-10-26 Thread David Foster
*Requesting reviewers* for the latest iteration of the PR to bulk-associate 
many-to-many relationships.

*The new PR to review*, which is *only a documentation change* showing how 
to bulk-associate many-to-many relationships, is here: 
https://github.com/django/django/pull/11948 

In case it's useful, the previous PR which actually introduced two new 
methods "add_relations" and "remove_relations" is here: 
https://github.com/django/django/pull/11899

It was previously argued that the implementation of "add_relations" and 
"remove_relations" was simple enough that only a documentation change might 
be needed. But after seeing the relatively complex boilerplate that the 
proposed documentation suggests, *I'm still leaning toward putting in 
dedicated "add_relations" and "remove_relations" methods.* Comments here? 
Put them on the umbrella Trac ticket: 
https://code.djangoproject.com/ticket/30828

Cheers,
David Foster | Seattle, WA, USA


On Sunday, October 13, 2019 at 3:39:12 PM UTC-7, David Foster wrote:
>
> Here's the link to the PR for review: 
> https://github.com/django/django/pull/11899
>
> (Apologies for the double-post)
>
> - David
>
> On Sunday, October 13, 2019 at 3:37:07 PM UTC-7, David Foster wrote:
>>
>> I've created a PR which is waiting for review, if someone has time.
>>
>> According to Trac, the next step is:
>>
>> For anyone except the patch author to review the patch using the patch 
>> review checklist 
>> <https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/submitting-patches/#patch-review-checklist>
>>  and 
>> either mark the ticket as "Ready for checkin" if everything looks good, or 
>> leave comments for improvement and mark the ticket as "Patch needs 
>> improvement".
>>
>> Thanks for any help.
>>
>> - David
>>
>> On Tuesday, October 1, 2019 at 11:06:15 PM UTC-7, David Foster wrote:
>>>
>>> Trac ticket created: https://code.djangoproject.com/ticket/30828
>>>
>>> On Tuesday, October 1, 2019 at 3:02:38 AM UTC-7, Tom Forbes wrote:
>>>>
>>>> Hey David,
>>>> I like this idea, while I don’t think the use case is common there have 
>>>> been a few times where I’ve needed this and got around it by 
>>>> creating/modifying the through model in bulk. Having a method that does 
>>>> this would be good IMO.
>>>>
>>>> Unless anyone has strong opinions against this then can you make a 
>>>> ticket? 
>>>>
>>>> Tom
>>>>
>>>> On 30 Sep 2019, at 02:14, David Foster  wrote:
>>>>
>>>> 
>>>> Here is another API variation I might suggest:
>>>>
>>>> M1.m2_set.add_pairs(*[(m1, m2), ...], assert_no_collisions=False)
>>>> # --- OR ---
>>>> M1.m2_set.add_pair_ids(*[(m1_id, m2_id), ...], assert_no_collisions=
>>>> False)
>>>>
>>>> This has the advantages of being more similar to the existing add() API 
>>>> and not requiring a special function import.
>>>>
>>>> For bulk_disassociate() the analogous API would be:
>>>>
>>>> M1.m2_set.remove_pairs(*[(m1, m2), ...])
>>>> # --- OR ---
>>>> M1.m2_set.remove_pair_ids(*[(m1_id, m2_id), ...])
>>>>
>>>> - David
>>>>
>>>> On Thursday, September 26, 2019 at 10:45:45 AM UTC-7, David Foster 
>>>> wrote:
>>>>>
>>>>> Given the following example model:
>>>>>
>>>>> class M1(models.Model):
>>>>> m2_set = models.ManyToManyField('M2')
>>>>>
>>>>> It is already possible to associate *one* M1 with *many* M2s with a 
>>>>> single DB query:
>>>>>
>>>>> m1.m2_set.add(*m2s)
>>>>>
>>>>> However it's more difficult to associate *many* M1s with *many* M2s, 
>>>>> particularly if you want to skip associations that already exist.
>>>>>
>>>>> # NOTE: Does NOT skip associations that already exist!
>>>>> m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
>>>>> M1_M2 = M1.m2_set.through
>>>>> M1_M2.objects.bulk_create([
>>>>> M1_M2(m1_id=m1_id, m2_id=m2_id)
>>>>> for (m1_id, m2_id) in
>>>>> m1_and_m2_id_tuples
>>>>> ])
>>>>>
>>>>> What if we could do something like the following instead:
>>>>>
>>>>> bulk_asso

Re: Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-10-13 Thread David Foster
Here's the link to the PR for review: 
https://github.com/django/django/pull/11899

(Apologies for the double-post)

- David

On Sunday, October 13, 2019 at 3:37:07 PM UTC-7, David Foster wrote:
>
> I've created a PR which is waiting for review, if someone has time.
>
> According to Trac, the next step is:
>
> For anyone except the patch author to review the patch using the patch 
> review checklist 
> <https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/submitting-patches/#patch-review-checklist>
>  and 
> either mark the ticket as "Ready for checkin" if everything looks good, or 
> leave comments for improvement and mark the ticket as "Patch needs 
> improvement".
>
> Thanks for any help.
>
> - David
>
> On Tuesday, October 1, 2019 at 11:06:15 PM UTC-7, David Foster wrote:
>>
>> Trac ticket created: https://code.djangoproject.com/ticket/30828
>>
>> On Tuesday, October 1, 2019 at 3:02:38 AM UTC-7, Tom Forbes wrote:
>>>
>>> Hey David,
>>> I like this idea, while I don’t think the use case is common there have 
>>> been a few times where I’ve needed this and got around it by 
>>> creating/modifying the through model in bulk. Having a method that does 
>>> this would be good IMO.
>>>
>>> Unless anyone has strong opinions against this then can you make a 
>>> ticket? 
>>>
>>> Tom
>>>
>>> On 30 Sep 2019, at 02:14, David Foster  wrote:
>>>
>>> 
>>> Here is another API variation I might suggest:
>>>
>>> M1.m2_set.add_pairs(*[(m1, m2), ...], assert_no_collisions=False)
>>> # --- OR ---
>>> M1.m2_set.add_pair_ids(*[(m1_id, m2_id), ...], assert_no_collisions=
>>> False)
>>>
>>> This has the advantages of being more similar to the existing add() API 
>>> and not requiring a special function import.
>>>
>>> For bulk_disassociate() the analogous API would be:
>>>
>>> M1.m2_set.remove_pairs(*[(m1, m2), ...])
>>> # --- OR ---
>>> M1.m2_set.remove_pair_ids(*[(m1_id, m2_id), ...])
>>>
>>> - David
>>>
>>> On Thursday, September 26, 2019 at 10:45:45 AM UTC-7, David Foster wrote:
>>>>
>>>> Given the following example model:
>>>>
>>>> class M1(models.Model):
>>>> m2_set = models.ManyToManyField('M2')
>>>>
>>>> It is already possible to associate *one* M1 with *many* M2s with a 
>>>> single DB query:
>>>>
>>>> m1.m2_set.add(*m2s)
>>>>
>>>> However it's more difficult to associate *many* M1s with *many* M2s, 
>>>> particularly if you want to skip associations that already exist.
>>>>
>>>> # NOTE: Does NOT skip associations that already exist!
>>>> m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
>>>> M1_M2 = M1.m2_set.through
>>>> M1_M2.objects.bulk_create([
>>>> M1_M2(m1_id=m1_id, m2_id=m2_id)
>>>> for (m1_id, m2_id) in
>>>> m1_and_m2_id_tuples
>>>> ])
>>>>
>>>> What if we could do something like the following instead:
>>>>
>>>> bulk_associate(M1.m2_set, [(m1, m2), ...])
>>>> # --- OR ---
>>>> bulk_associate_ids(M1.m2_set, [(m1_id, m2_id), ...])
>>>>
>>>> I propose to write and add a *bulk_associate()* method to Django. I 
>>>> also propose to add a paired *bulk_disassociate()* method.
>>>>
>>>> *1. Does this sound like a good idea in general?*
>>>>
>>>>
>>>> In more detail, I propose adding the specific APIs, importable from 
>>>> *django.db*:
>>>>
>>>> M1 = TypeVar('M1', bound=Model)  # M1 extends Model
>>>> M2 = TypeVar('M2', bound=Model)  # M2 extends Model
>>>>
>>>> def bulk_associate(
>>>> M1_m2_set: ManyToManyDescriptor,
>>>> m1_m2_tuples: 'List[Tuple[M1, M2]]',
>>>> *, assert_no_collisions: bool=True) -> None:
>>>> """
>>>> Creates many (M1, M2) associations with O(1) database queries.
>>>> 
>>>> If any requested associations already exist, then they will be left 
>>>> alone.
>>>> 
>>>> If you assert that none of the requested associations already exist,
>>>> you can pass assert_no_collisions=True to save 1 database query.
>>>> """
>>>>

Re: Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-10-13 Thread David Foster
I've created a PR which is waiting for review, if someone has time.

According to Trac, the next step is:

For anyone except the patch author to review the patch using the patch 
review checklist 
<https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/submitting-patches/#patch-review-checklist>
 and 
either mark the ticket as "Ready for checkin" if everything looks good, or 
leave comments for improvement and mark the ticket as "Patch needs 
improvement".

Thanks for any help.

- David

On Tuesday, October 1, 2019 at 11:06:15 PM UTC-7, David Foster wrote:
>
> Trac ticket created: https://code.djangoproject.com/ticket/30828
>
> On Tuesday, October 1, 2019 at 3:02:38 AM UTC-7, Tom Forbes wrote:
>>
>> Hey David,
>> I like this idea, while I don’t think the use case is common there have 
>> been a few times where I’ve needed this and got around it by 
>> creating/modifying the through model in bulk. Having a method that does 
>> this would be good IMO.
>>
>> Unless anyone has strong opinions against this then can you make a 
>> ticket? 
>>
>> Tom
>>
>> On 30 Sep 2019, at 02:14, David Foster  wrote:
>>
>> 
>> Here is another API variation I might suggest:
>>
>> M1.m2_set.add_pairs(*[(m1, m2), ...], assert_no_collisions=False)
>> # --- OR ---
>> M1.m2_set.add_pair_ids(*[(m1_id, m2_id), ...], assert_no_collisions=False
>> )
>>
>> This has the advantages of being more similar to the existing add() API 
>> and not requiring a special function import.
>>
>> For bulk_disassociate() the analogous API would be:
>>
>> M1.m2_set.remove_pairs(*[(m1, m2), ...])
>> # --- OR ---
>> M1.m2_set.remove_pair_ids(*[(m1_id, m2_id), ...])
>>
>> - David
>>
>> On Thursday, September 26, 2019 at 10:45:45 AM UTC-7, David Foster wrote:
>>>
>>> Given the following example model:
>>>
>>> class M1(models.Model):
>>> m2_set = models.ManyToManyField('M2')
>>>
>>> It is already possible to associate *one* M1 with *many* M2s with a 
>>> single DB query:
>>>
>>> m1.m2_set.add(*m2s)
>>>
>>> However it's more difficult to associate *many* M1s with *many* M2s, 
>>> particularly if you want to skip associations that already exist.
>>>
>>> # NOTE: Does NOT skip associations that already exist!
>>> m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
>>> M1_M2 = M1.m2_set.through
>>> M1_M2.objects.bulk_create([
>>> M1_M2(m1_id=m1_id, m2_id=m2_id)
>>> for (m1_id, m2_id) in
>>> m1_and_m2_id_tuples
>>> ])
>>>
>>> What if we could do something like the following instead:
>>>
>>> bulk_associate(M1.m2_set, [(m1, m2), ...])
>>> # --- OR ---
>>> bulk_associate_ids(M1.m2_set, [(m1_id, m2_id), ...])
>>>
>>> I propose to write and add a *bulk_associate()* method to Django. I 
>>> also propose to add a paired *bulk_disassociate()* method.
>>>
>>> *1. Does this sound like a good idea in general?*
>>>
>>>
>>> In more detail, I propose adding the specific APIs, importable from 
>>> *django.db*:
>>>
>>> M1 = TypeVar('M1', bound=Model)  # M1 extends Model
>>> M2 = TypeVar('M2', bound=Model)  # M2 extends Model
>>>
>>> def bulk_associate(
>>> M1_m2_set: ManyToManyDescriptor,
>>> m1_m2_tuples: 'List[Tuple[M1, M2]]',
>>> *, assert_no_collisions: bool=True) -> None:
>>> """
>>> Creates many (M1, M2) associations with O(1) database queries.
>>> 
>>> If any requested associations already exist, then they will be left 
>>> alone.
>>> 
>>> If you assert that none of the requested associations already exist,
>>> you can pass assert_no_collisions=True to save 1 database query.
>>> """
>>> pass
>>>
>>> def bulk_associate_ids(
>>> M1_m2_set: ManyToManyDescriptor,
>>> m1_m2_id_tuples: 'List[Tuple[Any, Any]]',
>>> *, assert_no_collisions: bool=True) -> None:
>>> pass
>>>
>>> If assert_no_collisions is False then (1 filter) query and (1 
>>> bulk_create) query will be performed.
>>> If assert_no_collisions is True then only (1 bulk_create) will be 
>>> performed.
>>>
>>> def bulk_disassociate(
>>> M1_m2_set: ManyToManyDescriptor,
>>> m1_m2_tuples: 'List[Tuple[M1

Re: Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-10-02 Thread David Foster
Trac ticket created: https://code.djangoproject.com/ticket/30828

On Tuesday, October 1, 2019 at 3:02:38 AM UTC-7, Tom Forbes wrote:
>
> Hey David,
> I like this idea, while I don’t think the use case is common there have 
> been a few times where I’ve needed this and got around it by 
> creating/modifying the through model in bulk. Having a method that does 
> this would be good IMO.
>
> Unless anyone has strong opinions against this then can you make a ticket? 
>
> Tom
>
> On 30 Sep 2019, at 02:14, David Foster > 
> wrote:
>
> 
> Here is another API variation I might suggest:
>
> M1.m2_set.add_pairs(*[(m1, m2), ...], assert_no_collisions=False)
> # --- OR ---
> M1.m2_set.add_pair_ids(*[(m1_id, m2_id), ...], assert_no_collisions=False)
>
> This has the advantages of being more similar to the existing add() API 
> and not requiring a special function import.
>
> For bulk_disassociate() the analogous API would be:
>
> M1.m2_set.remove_pairs(*[(m1, m2), ...])
> # --- OR ---
> M1.m2_set.remove_pair_ids(*[(m1_id, m2_id), ...])
>
> - David
>
> On Thursday, September 26, 2019 at 10:45:45 AM UTC-7, David Foster wrote:
>>
>> Given the following example model:
>>
>> class M1(models.Model):
>> m2_set = models.ManyToManyField('M2')
>>
>> It is already possible to associate *one* M1 with *many* M2s with a 
>> single DB query:
>>
>> m1.m2_set.add(*m2s)
>>
>> However it's more difficult to associate *many* M1s with *many* M2s, 
>> particularly if you want to skip associations that already exist.
>>
>> # NOTE: Does NOT skip associations that already exist!
>> m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
>> M1_M2 = M1.m2_set.through
>> M1_M2.objects.bulk_create([
>> M1_M2(m1_id=m1_id, m2_id=m2_id)
>> for (m1_id, m2_id) in
>> m1_and_m2_id_tuples
>> ])
>>
>> What if we could do something like the following instead:
>>
>> bulk_associate(M1.m2_set, [(m1, m2), ...])
>> # --- OR ---
>> bulk_associate_ids(M1.m2_set, [(m1_id, m2_id), ...])
>>
>> I propose to write and add a *bulk_associate()* method to Django. I also 
>> propose to add a paired *bulk_disassociate()* method.
>>
>> *1. Does this sound like a good idea in general?*
>>
>>
>> In more detail, I propose adding the specific APIs, importable from 
>> *django.db*:
>>
>> M1 = TypeVar('M1', bound=Model)  # M1 extends Model
>> M2 = TypeVar('M2', bound=Model)  # M2 extends Model
>>
>> def bulk_associate(
>> M1_m2_set: ManyToManyDescriptor,
>> m1_m2_tuples: 'List[Tuple[M1, M2]]',
>> *, assert_no_collisions: bool=True) -> None:
>> """
>> Creates many (M1, M2) associations with O(1) database queries.
>> 
>> If any requested associations already exist, then they will be left 
>> alone.
>> 
>> If you assert that none of the requested associations already exist,
>> you can pass assert_no_collisions=True to save 1 database query.
>> """
>> pass
>>
>> def bulk_associate_ids(
>> M1_m2_set: ManyToManyDescriptor,
>> m1_m2_id_tuples: 'List[Tuple[Any, Any]]',
>> *, assert_no_collisions: bool=True) -> None:
>> pass
>>
>> If assert_no_collisions is False then (1 filter) query and (1 
>> bulk_create) query will be performed.
>> If assert_no_collisions is True then only (1 bulk_create) will be 
>> performed.
>>
>> def bulk_disassociate(
>> M1_m2_set: ManyToManyDescriptor,
>> m1_m2_tuples: 'List[Tuple[M1, M2]]') -> None:
>> """
>> Deletes many (M1, M2) associations with O(1) database queries.
>> """
>> pass
>>
>> def bulk_disassociate_ids(
>> M1_m2_set: ManyToManyDescriptor,
>> m1_m2_id_tuples: 'List[Tuple[Any, Any]]') -> None:
>> pass
>>
>> The database connection corresponding to the M1_M2 through-table will be 
>> used.
>>
>> *2. Any comments on the specific API or capabilities?*
>>
>>
>> If this sounds good I'd be happy to open an item on Trac and submit a PR.
>>
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Django developers (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to django-d...@googlegroups.com .
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/django-developers/49b45d05-836f-4512-91b8-8e4dbb55f6a4%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/django-developers/49b45d05-836f-4512-91b8-8e4dbb55f6a4%40googlegroups.com?utm_medium=email_source=footer>
> .
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/a02a1a95-9da6-4212-8e79-595c60a2fa56%40googlegroups.com.


Re: Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-09-29 Thread David Foster
Here is another API variation I might suggest:

M1.m2_set.add_pairs(*[(m1, m2), ...], assert_no_collisions=False)
# --- OR ---
M1.m2_set.add_pair_ids(*[(m1_id, m2_id), ...], assert_no_collisions=False)

This has the advantages of being more similar to the existing add() API and 
not requiring a special function import.

For bulk_disassociate() the analogous API would be:

M1.m2_set.remove_pairs(*[(m1, m2), ...])
# --- OR ---
M1.m2_set.remove_pair_ids(*[(m1_id, m2_id), ...])

- David

On Thursday, September 26, 2019 at 10:45:45 AM UTC-7, David Foster wrote:
>
> Given the following example model:
>
> class M1(models.Model):
> m2_set = models.ManyToManyField('M2')
>
> It is already possible to associate *one* M1 with *many* M2s with a 
> single DB query:
>
> m1.m2_set.add(*m2s)
>
> However it's more difficult to associate *many* M1s with *many* M2s, 
> particularly if you want to skip associations that already exist.
>
> # NOTE: Does NOT skip associations that already exist!
> m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
> M1_M2 = M1.m2_set.through
> M1_M2.objects.bulk_create([
> M1_M2(m1_id=m1_id, m2_id=m2_id)
> for (m1_id, m2_id) in
> m1_and_m2_id_tuples
> ])
>
> What if we could do something like the following instead:
>
> bulk_associate(M1.m2_set, [(m1, m2), ...])
> # --- OR ---
> bulk_associate_ids(M1.m2_set, [(m1_id, m2_id), ...])
>
> I propose to write and add a *bulk_associate()* method to Django. I also 
> propose to add a paired *bulk_disassociate()* method.
>
> *1. Does this sound like a good idea in general?*
>
>
> In more detail, I propose adding the specific APIs, importable from 
> *django.db*:
>
> M1 = TypeVar('M1', bound=Model)  # M1 extends Model
> M2 = TypeVar('M2', bound=Model)  # M2 extends Model
>
> def bulk_associate(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_tuples: 'List[Tuple[M1, M2]]',
> *, assert_no_collisions: bool=True) -> None:
> """
> Creates many (M1, M2) associations with O(1) database queries.
> 
> If any requested associations already exist, then they will be left 
> alone.
> 
> If you assert that none of the requested associations already exist,
> you can pass assert_no_collisions=True to save 1 database query.
> """
> pass
>
> def bulk_associate_ids(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_id_tuples: 'List[Tuple[Any, Any]]',
> *, assert_no_collisions: bool=True) -> None:
> pass
>
> If assert_no_collisions is False then (1 filter) query and (1 bulk_create) 
> query will be performed.
> If assert_no_collisions is True then only (1 bulk_create) will be 
> performed.
>
> def bulk_disassociate(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_tuples: 'List[Tuple[M1, M2]]') -> None:
> """
> Deletes many (M1, M2) associations with O(1) database queries.
> """
> pass
>
> def bulk_disassociate_ids(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_id_tuples: 'List[Tuple[Any, Any]]') -> None:
> pass
>
> The database connection corresponding to the M1_M2 through-table will be 
> used.
>
> *2. Any comments on the specific API or capabilities?*
>
>
> If this sounds good I'd be happy to open an item on Trac and submit a PR.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/49b45d05-836f-4512-91b8-8e4dbb55f6a4%40googlegroups.com.


Re: Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-09-26 Thread David Foster
Errata: The proposed default value for assert_no_collisions is False rather 
than True, for safety.

On Thursday, September 26, 2019 at 10:45:45 AM UTC-7, David Foster wrote:
>
> Given the following example model:
>
> class M1(models.Model):
> m2_set = models.ManyToManyField('M2')
>
> It is already possible to associate *one* M1 with *many* M2s with a 
> single DB query:
>
> m1.m2_set.add(*m2s)
>
> However it's more difficult to associate *many* M1s with *many* M2s, 
> particularly if you want to skip associations that already exist.
>
> # NOTE: Does NOT skip associations that already exist!
> m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
> M1_M2 = M1.m2_set.through
> M1_M2.objects.bulk_create([
> M1_M2(m1_id=m1_id, m2_id=m2_id)
> for (m1_id, m2_id) in
> m1_and_m2_id_tuples
> ])
>
> What if we could do something like the following instead:
>
> bulk_associate(M1.m2_set, [(m1, m2), ...])
> # --- OR ---
> bulk_associate_ids(M1.m2_set, [(m1_id, m2_id), ...])
>
> I propose to write and add a *bulk_associate()* method to Django. I also 
> propose to add a paired *bulk_disassociate()* method.
>
> *1. Does this sound like a good idea in general?*
>
>
> In more detail, I propose adding the specific APIs, importable from 
> *django.db*:
>
> M1 = TypeVar('M1', bound=Model)  # M1 extends Model
> M2 = TypeVar('M2', bound=Model)  # M2 extends Model
>
> def bulk_associate(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_tuples: 'List[Tuple[M1, M2]]',
> *, assert_no_collisions: bool=True) -> None:
> """
> Creates many (M1, M2) associations with O(1) database queries.
> 
> If any requested associations already exist, then they will be left 
> alone.
> 
> If you assert that none of the requested associations already exist,
> you can pass assert_no_collisions=True to save 1 database query.
> """
> pass
>
> def bulk_associate_ids(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_id_tuples: 'List[Tuple[Any, Any]]',
> *, assert_no_collisions: bool=True) -> None:
> pass
>
> If assert_no_collisions is False then (1 filter) query and (1 bulk_create) 
> query will be performed.
> If assert_no_collisions is True then only (1 bulk_create) will be 
> performed.
>
> def bulk_disassociate(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_tuples: 'List[Tuple[M1, M2]]') -> None:
> """
> Deletes many (M1, M2) associations with O(1) database queries.
> """
> pass
>
> def bulk_disassociate_ids(
> M1_m2_set: ManyToManyDescriptor,
> m1_m2_id_tuples: 'List[Tuple[Any, Any]]') -> None:
> pass
>
> The database connection corresponding to the M1_M2 through-table will be 
> used.
>
> *2. Any comments on the specific API or capabilities?*
>
>
> If this sounds good I'd be happy to open an item on Trac and submit a PR.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/74c15393-9312-48bc-ac06-a3d4360b3432%40googlegroups.com.


Feature idea: bulk_associate: Add ManyToMany relationships in bulk

2019-09-26 Thread David Foster
Given the following example model:

class M1(models.Model):
m2_set = models.ManyToManyField('M2')

It is already possible to associate *one* M1 with *many* M2s with a single 
DB query:

m1.m2_set.add(*m2s)

However it's more difficult to associate *many* M1s with *many* M2s, 
particularly if you want to skip associations that already exist.

# NOTE: Does NOT skip associations that already exist!
m1_and_m2_id_tuples = [(m1_id, m2_id), ...]
M1_M2 = M1.m2_set.through
M1_M2.objects.bulk_create([
M1_M2(m1_id=m1_id, m2_id=m2_id)
for (m1_id, m2_id) in
m1_and_m2_id_tuples
])

What if we could do something like the following instead:

bulk_associate(M1.m2_set, [(m1, m2), ...])
# --- OR ---
bulk_associate_ids(M1.m2_set, [(m1_id, m2_id), ...])

I propose to write and add a *bulk_associate()* method to Django. I also 
propose to add a paired *bulk_disassociate()* method.

*1. Does this sound like a good idea in general?*


In more detail, I propose adding the specific APIs, importable from 
*django.db*:

M1 = TypeVar('M1', bound=Model)  # M1 extends Model
M2 = TypeVar('M2', bound=Model)  # M2 extends Model

def bulk_associate(
M1_m2_set: ManyToManyDescriptor,
m1_m2_tuples: 'List[Tuple[M1, M2]]',
*, assert_no_collisions: bool=True) -> None:
"""
Creates many (M1, M2) associations with O(1) database queries.

If any requested associations already exist, then they will be left 
alone.

If you assert that none of the requested associations already exist,
you can pass assert_no_collisions=True to save 1 database query.
"""
pass

def bulk_associate_ids(
M1_m2_set: ManyToManyDescriptor,
m1_m2_id_tuples: 'List[Tuple[Any, Any]]',
*, assert_no_collisions: bool=True) -> None:
pass

If assert_no_collisions is False then (1 filter) query and (1 bulk_create) 
query will be performed.
If assert_no_collisions is True then only (1 bulk_create) will be performed.

def bulk_disassociate(
M1_m2_set: ManyToManyDescriptor,
m1_m2_tuples: 'List[Tuple[M1, M2]]') -> None:
"""
Deletes many (M1, M2) associations with O(1) database queries.
"""
pass

def bulk_disassociate_ids(
M1_m2_set: ManyToManyDescriptor,
m1_m2_id_tuples: 'List[Tuple[Any, Any]]') -> None:
pass

The database connection corresponding to the M1_M2 through-table will be 
used.

*2. Any comments on the specific API or capabilities?*


If this sounds good I'd be happy to open an item on Trac and submit a PR.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/3ca80bdc-8bc9-4304-8723-6e23302e1364%40googlegroups.com.


Improving usefulness of management command help output via reordering options

2018-04-05 Thread David Foster
Currently if you run a custom management command with --help, you will get 
output that looks like:

$ python3 manage.py import_layout_calendar --help

usage: manage.py import_layout_calendar [-h] [--version] [-v {0,1,2,3}]

[--settings SETTINGS]

[--pythonpath PYTHONPATH]

[--traceback] [--no-color]

[--simulate] [--no-strict] [--debug]

[--replace] [--fast-validate]

[args [args ...]]


Imports the calendar for a layout in the database.


positional arguments:

  args


optional arguments:

  -h, --helpshow this help message and exit

  --version show program's version number and exit

  -v {0,1,2,3}, --verbosity {0,1,2,3}

Verbosity level; 0=minimal output, 1=normal output,

2=verbose output, 3=very verbose output

  --settings SETTINGS   The Python path to a settings module, e.g.

"myproject.settings.main". If this isn't provided, 
the

DJANGO_SETTINGS_MODULE environment variable will be

used.

  --pythonpath PYTHONPATH

A directory to add to the Python path, e.g.

"/home/djangoprojects/myproject".

  --traceback   Raise on CommandError exceptions

  --no-colorDon't colorize the command output.

  --simulateSuppress all database modifications

  --no-strict, -S   Do not promote all warnings to errors

  --debug   Perform all validating and importing live, without

capturing output

  --replace Delete the current layout overlay before importing.

  --fast-validate   Disables full simulated import as part of validation

I have highlighted in yellow the useful information specific to the command 
that is *not* boilerplate. Notice that most of this yellow text is at the 
end of the output, with the boilerplate dominating what the user reads 
first.

I propose reordering the options in the output so that the useful 
information is at the *beginning* rather than the end, so that it looks 
more like the following:

$ python3 manage.py import_layout_calendar --help

usage: manage.py import_layout_calendar [-h] [--simulate] [--no-strict]

[--debug] [--replace]

[--fast-validate] [--version]

[-v {0,1,2,3}] [--settings SETTINGS]

[--pythonpath PYTHONPATH]

[--traceback] [--no-color]

[args [args ...]]


Imports the calendar for a layout in the database.


positional arguments:

  args   |  



optional arguments:

  -h, --helpshow this help message and exit

  --simulateSuppress all database modifications

  --no-strict, -S   Do not promote all warnings to errors

  --debug   Perform all validating and importing live, without

capturing output

  --replace Delete the current layout overlay before importing.

  --fast-validate   Disables full simulated import as part of validation

  --version show program's version number and exit

  -v {0,1,2,3}, --verbosity {0,1,2,3}

Verbosity level; 0=minimal output, 1=normal output,

2=verbose output, 3=very verbose output

  --settings SETTINGS   The Python path to a settings module, e.g.

"myproject.settings.main". If this isn't provided, 
the

DJANGO_SETTINGS_MODULE environment variable will be

used.

  --pythonpath PYTHONPATH

A directory to add to the Python path, e.g.

"/home/djangoprojects/myproject".

  --traceback   Raise on CommandError exceptions

  --no-colorDon't colorize the command output.

Thoughts?

--
David Foster | Seattle, WA, USA

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/ca99208e-ddda-42b4-bf25-57f526dc5252%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Adding a tutorial for Channels

2018-02-05 Thread David Foster
This weekend I spent several hours getting Channels configured to run a 
simple chat server . 
This was an experiment I wanted to do before trying to integrate Channels 
into a much larger project.

It would have taken me a lot less time if there had been a tutorial in the 
official 
Channels docs  similar 
in style to the Django tutorial 
 that showed how 
to configure Channels and use it to work with WebSockets while also 
touching on the important Channels concepts and linking to reference 
documentation where appropriate. So I'd like to offer to write such a 
tutorial.

Would this be a welcome contribution?

- David

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/9b68e3cf-76de-4506-9733-00b2d640b1e2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.