Re: `Model.validate_unique` excluding partial unique constraint

2021-06-14 Thread Gaga Ro
Thanks, it clears things a lot.

I'll try my hand at it when I'll have some more time available.

Le jeudi 10 juin 2021 à 06:00:17 UTC+2, charettes a écrit :

> Alright so here's for a few hints about I believe things should be done.
>
> First things first Lookup must be made a subclass of Expression which is 
> being worked on[0].
>
> Ideally Q would also be made a subclass of Expression but that's likely a 
> big can of worms so I'd focus on implementing it for Q only at first.
>
> Now for the compiler part. Things are bit tricky here as these expressions 
> are not going to be bound to a model/table and most of the sql.Query and 
> resolve_expression machinery revolves around the availability of a 
> Query.model: models.Model property. I think there's two solutions here:
>
> 1. Adapt sql.Query so it can be *unbounded* meaning that it's .model 
> property type would change from models.Model to Optional[models.Model]. 
> 2. Follow the sql.RawQuery route and define a new sql.UnboundQuery class 
> that *looks* like a Query but doesn't allow any form of column references 
> or JOINs or filters (WHERE).
>
> In both cases the Query like object should prevent any form of column 
> references and JOINs with a comprehensible error messages (e.g. in 
> resolve_ref 
> and setup_join if go with 1.). I have a feeling 2. is easier to implement 
> but 1. seems like it could be a much more rewarding experience for you and 
> the community as you'll have to special case a couple of long lived 
> assumptions in django.db.models.sql.
>
> Depending on whether you choose the 1. or 2. you'll have to implement a 
> way for database backends to specify how to *wrap* the provided expression 
> in a SELECT statement. Most databases won't require any special casing but 
> I know that Oracle will require the addition of a trailing "DUAL" clause 
> (SELECT ... FROM DUAL)[1] and possibly some special casing of expressions 
> such as exists but there's already pre-existing logic for that[2]. If you 
> go with 1. this can be done by returning a backend specific string in 
> SQLCompiler.get_from_clause when self.query.alias_map is empty[3].
>
> In the end the call stack should be (assuming 1. is followed):
>
> Q.check(self, using):
>   query = Query()
>   query.add_annotations(self, '_check')
>   query.set_values('_check')
>   compiler = query.get_compiler(using=db)
>   result = compiler.execute_sql(SINGLE)
>   return bool(result[0])
>
> I hope this clears things up a bit!
>
> Cheers,
> Simon
>
> [0] https://github.com/django/django/pull/14494
> [1] https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries009.htm
> [2] 
> https://github.com/django/django/blob/ed3af3ff4b3cfb72de598f1b39a1028ba3826efb/django/db/models/expressions.py#L382-L389
> [3] 
> https://github.com/django/django/blob/ed3af3ff4b3cfb72de598f1b39a1028ba3826efb/django/db/models/sql/compiler.py#L797-L810
>
> Le mercredi 2 juin 2021 à 11:36:02 UTC-4, gaga...@gmail.com a écrit :
>
>> Thanks for the thorough answer. I also realize now that it worked in my 
>> app only because of another side effect when my instance was saved..
>>
>> I started to take a look at the ORM part where the check method should be 
>> implemented as I'm not used to it. Shouldn't .check() be implemented on Q 
>> and not on Expression? Or are you including Lookup / Q in it?
>>
>> Then I'd guess it's just a matter of calling as_sql() from each part and 
>> assemble them. Everythings we need seems to be done in Query and we can't 
>> use it as it has to be linked to a model, so we would have to redo it? 
>> Although as_sql needs a compiler which itself needs a query. I admit I'm a 
>> bit lost in all those classes, everything seems to be too much linked to 
>> the models to do something without one.
>>
>> If you have any more hints as to where I should look, thanks again.
>> Le mercredi 2 juin 2021 à 00:33:12 UTC+2, charettes a écrit :
>>
>>> Hello there,
>>>
>>> Partial unique constraints are currently not supported during validation 
>>> for reasons described in this ticket[0].
>>>
>>> For example (inspired by this Github comment[1]), if you define the 
>>> following model
>>>
>>> class Article(models.Model):
>>> slug = models.CharField(max_length=100)
>>> deleted_at = models.DateTimeField(null=True)
>>>
>>> class Meta:
>>> constraints = [
>>> UniqueConstraint('slug', condition=Q(deleted_at=None), 
>>> name='unique_slug'),
>>> ]
>>>
>>> Then validate_unique must perform the following query to determine if 
>>> the constraint is violated
>>>
>>> SELECT NOT (%(deleted_at)s IS NULL) OR NOT EXISTS(SELECT 1 FROM article 
>>> WHERE NOT id = %(id)s AND slug = %(slug)s AND deleted_at IS NULL)
>>>
>>> In other words, the validation of a partial unique constraint must check 
>>> that either of these conditions are true
>>> 1. The provided instance doesn't match the condition
>>> 2. There's no existing rows matching the unique constraint (excluding 
>>> the current ins

A base compression middleware class

2021-06-14 Thread Illia Volochii
Hi all,

There is `GZipMiddleware` that compresses response bodies using gzip.
https://github.com/django/django/blob/main/django/middleware/gzip.py

But there are other algorithms supported by browsers (e.g., Brotli).
At the moment, if somebody wants to add support for any of them in a
Django project, one has to create a new middleware class redefining
all the logic processing responses.

I propose simplifying the process by creating
`BaseCompressionMiddleware`. Its subclasses would just need to define
a tuple of supported compressors.

The tuple elements could be instances of such a data class:
```
@dataclass
class Compressor:
name: str
compress_string_func: Callable[[bytes], bytes]
compress_sequence_func: Optional[
Callable[[Iterable[bytes]], Iterator[bytes]]
] = None

def is_accepted_by(self, accept_encoding):
return bool(re.search(rf'\b{self.name}\b', accept_encoding))
```


And the class could select a compressor using such code:
```
@classmethod
def select_compressor(cls, request, response):
ae = request.META.get('HTTP_ACCEPT_ENCODING', '')
for compressor in cls.compressors:
if (
compressor.is_accepted_by(ae)
and (not response.streaming or 
compressor.compress_sequence_func)
):
return compressor
```


Note, in the example `compress_string_func` is required and
`compress_sequence_func` is optional. I think that
`compress_string_func` would usually be a library function, and
`compress_sequence_func` would require some custom logic. And we
should not force users to define the latter.

`GZipMiddleware` could be changed to
```
gzip_compressor = Compressor(
name='gzip',
compress_string_func=compress_string,
compress_sequence_func=compress_sequence,
)

class GZipMiddleware(BaseCompressionMiddleware):
compressors = (gzip_compressor,)
```


And someone wanting to support both Brotli and gzip could define:
```
import brotli
from django.middleware.gzip import gzip_compressor

brotli_compressor = Compressor(
name='br',
compress_string_func=brotli.compress,
)

class CompressionMiddleware(BaseCompressionMiddleware)
compressors = (brotli_compressor, gzip_compressor)
```


Please let me know what you think about such an enhancement.
I will be happy to create a pull request if this is accepted.

Thanks,
Illia

-- 
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/49535d7e-758c-4acf-a9da-831ccfa1d17bn%40googlegroups.com.


Re: A base compression middleware class

2021-06-14 Thread Curtis Maloney
Hi Illia,

I like the idea here, and your design looks sensible at first blush, but I feel 
conflicted.

As much as I like the idea of allowing more flexibility and future-proofing 
this middleware,
AIUI the gzip middleware is generally discouraged, as it's typically more 
efficient to have your web server do this work.

I'd be more tempted to deprecate GzipMiddleware in core, and move this feature 
into a 3rd party app.

An additional hook for your interface: a function to test if this compressor 
should be used on this response. For instance, the gzip middleware has a 
minimum size below which it's deemed not worth the effort to try to compress.

--
Curtis


On Tue, 15 Jun 2021, at 04:49, Illia Volochii wrote:
> Hi all,
> 
> There is `GZipMiddleware` that compresses response bodies using gzip.
> https://github.com/django/django/blob/main/django/middleware/gzip.py
> 
> But there are other algorithms supported by browsers (e.g., Brotli).
> At the moment, if somebody wants to add support for any of them in a
> Django project, one has to create a new middleware class redefining
> all the logic processing responses.
> 
> I propose simplifying the process by creating
> `BaseCompressionMiddleware`. Its subclasses would just need to define
> a tuple of supported compressors.
> 
> The tuple elements could be instances of such a data class:
> ```
> @dataclass
> class Compressor:
> name: str
> compress_string_func: Callable[[bytes], bytes]
> compress_sequence_func: Optional[
> Callable[[Iterable[bytes]], Iterator[bytes]]
> ] = None
> 
> def is_accepted_by(self, accept_encoding):
> return bool(re.search(rf'\b{self.name}\b', accept_encoding))
> ```
> 
> 
> And the class could select a compressor using such code:
> ```
> @classmethod
> def select_compressor(cls, request, response):
> ae = request.META.get('HTTP_ACCEPT_ENCODING', '')
> for compressor in cls.compressors:
> if (
> compressor.is_accepted_by(ae)
> and (not response.streaming or compressor.compress_sequence_func)
> ):
> return compressor
> ```
> 
> 
> Note, in the example `compress_string_func` is required and
> `compress_sequence_func` is optional. I think that
> `compress_string_func` would usually be a library function, and
> `compress_sequence_func` would require some custom logic. And we
> should not force users to define the latter.
> 
> `GZipMiddleware` could be changed to
> ```
> gzip_compressor = Compressor(
> name='gzip',
> compress_string_func=compress_string,
> compress_sequence_func=compress_sequence,
> )
> 
> class GZipMiddleware(BaseCompressionMiddleware):
> compressors = (gzip_compressor,)
> ```
> 
> 
> And someone wanting to support both Brotli and gzip could define:
> ```
> import brotli
> from django.middleware.gzip import gzip_compressor
> 
> brotli_compressor = Compressor(
> name='br',
> compress_string_func=brotli.compress,
> )
> 
> class CompressionMiddleware(BaseCompressionMiddleware)
> compressors = (brotli_compressor, gzip_compressor)
> ```
> 
> 
> Please let me know what you think about such an enhancement.
> I will be happy to create a pull request if this is accepted.
> 
> Thanks,
> Illia
> 
> 
> 

> -- 
> 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/49535d7e-758c-4acf-a9da-831ccfa1d17bn%40googlegroups.com
>  
> .

-- 
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/c4cef215-7cae-4c6b-a9b2-7b4785b3a9bf%40www.fastmail.com.


Re: Default change password UX: area for improvements

2021-06-14 Thread Thibaud Colas
Good idea! I think it’s worth mentioning as well the current link’s styles 
wouldn’t pass accessibility guidelines, as there isn’t enough contrast 
between the link color and surrounding text color, and the choice of words 
doesn’t make the link very identifiable either.

This would need to remain an `` tag so it has the best possible 
semantics – I think styling this like a button would make this as easily 
identifiable as possible, but from the perspective of the accessibility 
issues it would be enough to add an underline to the link style and make 
sure the link text makes sense without context.

Cheers,

Thibaud

On Monday, 7 June 2021 at 22:32:36 UTC+1 in...@markusholtermann.eu wrote:

> Hi Federico,
>
> this is a good idea. Could you check if there's a ticket about this on 
> https://code.djangoproject.com/ already, and if not, possibly open one. 
> That would be much appreciated. Thank you!
>
> Cheers,
>
> Markus
>
> On Mon, Jun 7, 2021, at 11:12 PM, Federico Capoano wrote:
> > Hey everyone,
> > 
> > Given the following text in the user change page:
> > 
> > *Raw passwords are not stored, so there is no way to see this user’s 
> > password, but you can change the password using this form.*
> > *
> > *
> > Linking the change user password form using the word "this form" is on 
> > the same level of links with "click here", which is notoriously bad UX 
> > and I believe the django community is made of a lot of experts so there 
> > shouldn't be the need to explain this here.
> > 
> > However, we can do better than just changing the link form, wouldn't it 
> > be possible to add a link styled as a button?
> > 
> > Eg:
> > 
> > Raw passwords are not stored, so there is no way to see this user’s 
> > password, but the password can be changed.
> > 
> > *Change the password*
> > 
> > BTW, this idea came from this issue: 
> > https://github.com/openwisp/openwisp-radius/issues/265.
> > We can easily fix it in our django app but I thought it may be good to 
> > raise this here so we may fix it at the root instead.
> > 
> > Best regards
> > Federico Capoano
> > 
> > -- 
> > 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-develop...@googlegroups.com.
> > To view this discussion on the web visit 
> > 
> https://groups.google.com/d/msgid/django-developers/CAERYH6VUm-bRsHkY_1LX9TwpJvMW%2B-Eyqf0Uye6FL%2B_Pb_%3D%2BQw%40mail.gmail.com
>  
> <
> https://groups.google.com/d/msgid/django-developers/CAERYH6VUm-bRsHkY_1LX9TwpJvMW%2B-Eyqf0Uye6FL%2B_Pb_%3D%2BQw%40mail.gmail.com?utm_medium=email&utm_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/9410e840-37b2-47f1-b0b1-d89732c7463fn%40googlegroups.com.


Accessibility standards & contribution guidelines for Django

2021-06-14 Thread Thibaud Colas
Hi django-developers,

This is a long-overdue follow up to DEP 11 
 
and the creation of Django’s accessibility team. As part of implementing 
what this DEP outlines, we need to discuss what accessibility standards 
should apply to Django, among other things. In my opinion this is both a 
matter of deciding what pre-existing standards / guidelines would be 
relevant in theory, but also how to make sure whichever ones we choose are 
achievable to follow in practice. We need to find some consensus on what 
Django as a project should aim for, so our team and all contributors can 
take it into consideration, and in particular so we can document what we 
want to commit to as a reference for accessibility work.

Here are accessibility team responsibilities 

 
I would like to bring up for discussion in particular:

   - *Deciding on any relevant accessibility guidelines to follow, such as 
   WCAG, and at which conformance level.*
   - *Coordinating regular manual accessibility audits on all relevant 
   projects.*
   - *Writing and maintaining documentation relating to accessibility, such 
   as a statement of commitment to accessibility issues, and contribution 
   guidelines.*

Now I’ll try to outline what I’d suggest for each area, which people here 
are very welcome to disagree with as relevant. 

Accessibility guidelines

In my opinion, the most important standard for *_sites built with Django_* 
is WCAG 2.1, AA level. Anything resulting in AA level issues should be 
considered as a bug. Wherever possible, we should also strive to not get in 
the way of sites built with Django aiming for AAA level compliance.

For the Django admin, I would recommend the same target of WCAG 2.1 AA 
level as a baseline, aiming for AAA wherever possible. For example, the 
link text issue discussed in Default change password UX: area for 
improvements 
 is 
a case where I’d expect us to want to follow WCAG SC 2.4.9: Link Purpose 
(Link Only) 
, 
even though it’s level AAA.

This distinction between "sites built with Django" and the "Django admin" 
highlights an important point, which is that for lots of sites the Django 
admin would be what accessibility standards call an authoring tool. There 
is a separate standard for authoring tools that builds upon WCAG 2.1: ATAG 
2.0. I think we should also explicitly aim for compliance with ATAG 2.0 
wherever possible, as it has a lot of useful guidelines we could follow for 
the Django admin. For Django itself, there might be very little to do to 
implement those guidelines, but for the wider ecosystem of CMS-like 
features in Django it would make a clear difference if Django was clearly 
defined as an authoring tool. WCAG itself is a very thin baseline as 
standards go, so anything that goes beyond it is well worth considering in 
my opinion.

For everything else within the Django ecosystem – the documentation, the 
djangoproject.com website, the bug tracker, IRC, plain-text documentation, 
etc. – we should follow the exact same standard. I would also really like 
to see the same standard being explicitly recommended for consideration for 
third-party Django projects (similarly to Django projects wanting to 
provide the same level of browser and language support as Django itself, I 
imagine). This probably warrants a separate conversation but I would also 
recommend the Django Software Foundation explicitly mandating the same 
standard for any project or event it endorses.

It’s worth mentioning as well that the next version of WCAG, WCAG 2.2, is 
due to be released by the end of 2021. I’d recommend we don’t aim for 
support with WCAG 2.2 right now, but do so as soon as it’s released. In the 
meantime, we should also consider any WCAG 2.2-only guideline wherever 
reasonable (for example in audits, design of new features, etc.).

Contribution guidelines

Aiming for compliance with specific standards is good, but we shouldn’t 
expect Django contributors to be familiar with WCAG, or any other standard. 
I think it’s important we update the contribution guidelines to propose 
practical support targets:


   - *Which automated or semi-automated testing tools to use when testing 
   changes to Django*. My go-to is anything Axe 
-based, 
   as it’s ubiquitous, and has very few false positives. In particular with 
   the Accessibility Insights  browser 
   extension.
   - *Which assistive technologies should we explicitly strive to test 
   Django with, so contributors have clear guidance on how to test their 
   changes*. For example here are the testing guidelines for public sector 
   sites in the UK