Trees | Indices | Help |
---|
|
1 import os 2 import time 3 import datetime 4 5 from sqlalchemy import and_ 6 from sqlalchemy.sql import func 7 from sqlalchemy import asc, desc 8 from sqlalchemy.event import listens_for 9 from sqlalchemy.orm.attributes import NEVER_SET 10 from sqlalchemy.orm.exc import NoResultFound 11 from sqlalchemy.orm.attributes import get_history 12 13 from copr_common.enums import ActionTypeEnum, BackendResultEnum 14 from coprs import db 15 from coprs import exceptions 16 from coprs import helpers 17 from coprs import models 18 from coprs.exceptions import MalformedArgumentException, BadRequest 19 from coprs.logic import users_logic 20 from coprs.whoosheers import CoprWhoosheer 21 from coprs.helpers import fix_protocol_for_backend 22 23 from coprs.logic.actions_logic import ActionsLogic 24 from coprs.logic.users_logic import UsersLogic28 """ 29 Used for manipulating Coprs. 30 31 All methods accept user object as a first argument, 32 as this may be needed in future. 33 """ 34 35 @classmethod38637 """ Return all coprs without those which are deleted. """ 38 query = (db.session.query(models.Copr) 39 .join(models.Copr.user) 40 .options(db.contains_eager(models.Copr.user)) 41 .filter(models.Copr.deleted == False)) 42 return query43 44 @classmethod 47 48 @classmethod50 query = (query.outerjoin(models.Copr.builds) 51 .options(db.contains_eager(models.Copr.builds)) 52 .order_by(models.Build.submitted_on.desc())) 53 return query54 55 @classmethod57 query = (query.outerjoin(*models.Copr.mock_chroots.attr) 58 .options(db.contains_eager(*models.Copr.mock_chroots.attr)) 59 .order_by(models.MockChroot.os_release.asc()) 60 .order_by(models.MockChroot.os_version.asc()) 61 .order_by(models.MockChroot.arch.asc())) 62 return query63 64 @classmethod66 with_builds = kwargs.get("with_builds", False) 67 with_mock_chroots = kwargs.get("with_mock_chroots", False) 68 69 query = ( 70 cls.get_all() 71 .filter(models.User.username == username) 72 ) 73 74 if with_builds: 75 query = cls.attach_build(query) 76 77 if with_mock_chroots: 78 query = cls.attach_mock_chroots(query) 79 80 return query81 82 @classmethod84 with_builds = kwargs.get("with_builds", False) 85 with_mock_chroots = kwargs.get("with_mock_chroots", False) 86 87 query = ( 88 cls.get_all() 89 .filter(models.Copr.group_id == group_id) 90 ) 91 92 if with_builds: 93 query = cls.attach_build(query) 94 95 if with_mock_chroots: 96 query = cls.attach_mock_chroots(query) 97 98 return query99 100 @classmethod102 query = cls.get_multiple_by_username(username, **kwargs) 103 query = query.filter(models.Copr.name == coprname) 104 return query105 106 @classmethod108 query = cls.get_multiple_by_group_id(group_id, **kwargs) 109 query = query.filter(models.Copr.name == coprname) 110 return query111 112 @classmethod114 query = ( 115 db.session.query(models.Copr) 116 .join(models.Copr.user) 117 .outerjoin(models.Group) 118 .options(db.contains_eager(models.Copr.user)) 119 ) 120 121 if not include_deleted: 122 query = query.filter(models.Copr.deleted.is_(False)) 123 124 if not include_unlisted_on_hp: 125 query = query.filter(models.Copr.unlisted_on_hp.is_(False)) 126 127 return query128 129 @classmethod131 if desc: 132 query = query.order_by(models.Copr.id.desc()) 133 else: 134 query = query.order_by(models.Copr.id.asc()) 135 return query136 137 # user_relation="owned", username=username, with_mock_chroots=False 138 @classmethod140 query = cls.get_multiple(include_unlisted_on_hp=include_unlisted_on_hp) 141 return query.filter(models.User.username == username)142 143 @classmethod 146 147 @classmethod149 # should be already joined with the User table 150 return query.filter(models.User.username == username)151 152 @classmethod154 # should be already joined with the Group table 155 return query.filter(models.Group.name == group_name)156 157 @classmethod 160 161 @classmethod 164 165 @classmethod167 return (query.outerjoin(models.Copr.builds) 168 .options(db.contains_eager(models.Copr.builds)) 169 .order_by(models.Build.submitted_on.desc()))170 171 @classmethod173 return (query.outerjoin(*models.Copr.mock_chroots.attr) 174 .options(db.contains_eager(*models.Copr.mock_chroots.attr)) 175 .order_by(models.MockChroot.os_release.asc()) 176 .order_by(models.MockChroot.os_version.asc()) 177 .order_by(models.MockChroot.arch.asc()))178 179 @classmethod 182 183 @classmethod185 if user.admin: 186 db.session.add(copr) 187 pass 188 else: 189 raise exceptions.InsufficientRightsException( 190 "User is not a system admin")191 192 @classmethod194 query = (models.Copr.query.order_by(desc(models.Copr.created_on)) 195 .join(models.User) 196 .filter(models.Copr.deleted == False)) 197 if "/" in search_string: # copr search by its full name 198 if search_string[0] == '@': # searching for @group/project 199 group_name = "%{}%".format(search_string.split("/")[0][1:]) 200 project = "%{}%".format(search_string.split("/")[1]) 201 query = query.filter(and_(models.Group.name.ilike(group_name), 202 models.Copr.name.ilike(project), 203 models.Group.id == models.Copr.group_id)) 204 query = query.order_by(asc(func.length(models.Group.name)+func.length(models.Copr.name))) 205 else: # searching for user/project 206 user_name = "%{}%".format(search_string.split("/")[0]) 207 project = "%{}%".format(search_string.split("/")[1]) 208 query = query.filter(and_(models.User.username.ilike(user_name), 209 models.Copr.name.ilike(project), 210 models.User.id == models.Copr.user_id)) 211 query = query.order_by(asc(func.length(models.User.username)+func.length(models.Copr.name))) 212 else: # fulltext search 213 query = query.whooshee_search(search_string, whoosheer=CoprWhoosheer, order_by_relevance=100) 214 return query215 216 @classmethod217 - def add(cls, user, name, selected_chroots, repos=None, description=None, 218 instructions=None, check_for_duplicates=False, group=None, persistent=False, 219 auto_prune=True, use_bootstrap_container=False, follow_fedora_branching=False, **kwargs):220 221 if not user.admin and persistent: 222 raise exceptions.NonAdminCannotCreatePersistentProject() 223 224 if not user.admin and not auto_prune: 225 raise exceptions.NonAdminCannotDisableAutoPrunning() 226 227 # form validation checks for duplicates 228 cls.new(user, name, group, check_for_duplicates=check_for_duplicates) 229 230 copr = models.Copr(name=name, 231 repos=repos or u"", 232 user=user, 233 description=description or u"", 234 instructions=instructions or u"", 235 created_on=int(time.time()), 236 persistent=persistent, 237 auto_prune=auto_prune, 238 use_bootstrap_container=use_bootstrap_container, 239 follow_fedora_branching=follow_fedora_branching, 240 **kwargs) 241 242 243 if group is not None: 244 UsersLogic.raise_if_not_in_group(user, group) 245 copr.group = group 246 247 copr_dir = models.CoprDir( 248 main=True, 249 name=name, 250 copr=copr) 251 252 db.session.add(copr_dir) 253 db.session.add(copr) 254 255 CoprChrootsLogic.new_from_names( 256 copr, selected_chroots) 257 258 db.session.flush() 259 ActionsLogic.send_create_gpg_key(copr) 260 261 return copr262 263 @classmethod265 if check_for_duplicates: 266 if group is None and cls.exists_for_user(user, copr_name).all(): 267 raise exceptions.DuplicateException( 268 "Copr: '{0}/{1}' already exists".format(user.name, copr_name)) 269 elif group: 270 if cls.exists_for_group(group, copr_name).all(): 271 db.session.rollback() 272 raise exceptions.DuplicateException( 273 "Copr: '@{0}/{1}' already exists".format(group.name, copr_name))274 275 @classmethod277 # we should call get_history before other requests, otherwise 278 # the changes would be forgotten 279 if get_history(copr, "name").has_changes(): 280 raise MalformedArgumentException("Change name of the project is forbidden") 281 282 users_logic.UsersLogic.raise_if_cant_update_copr( 283 user, copr, "Only owners and admins may update their projects.") 284 285 if not user.admin and not copr.auto_prune: 286 raise exceptions.NonAdminCannotDisableAutoPrunning() 287 288 db.session.add(copr)289 290 @classmethod292 """ 293 Deletes copr without termination of ongoing builds. 294 """ 295 cls.raise_if_cant_delete(user, copr) 296 # TODO: do we want to dump the information somewhere, so that we can 297 # search it in future? 298 cls.raise_if_unfinished_blocking_action( 299 copr, "Can't delete this project," 300 " another operation is in progress: {action}") 301 302 ActionsLogic.send_delete_copr(copr) 303 CoprDirsLogic.delete_all_by_copr(copr) 304 305 copr.deleted = True 306 return copr307 308 @classmethod310 existing = (models.Copr.query 311 .order_by(desc(models.Copr.created_on)) 312 .filter(models.Copr.name == coprname) 313 .filter(models.Copr.user_id == user.id)) 314 315 if not incl_deleted: 316 existing = existing.filter(models.Copr.deleted == False) 317 318 return cls.filter_without_group_projects(existing)319 320 @classmethod322 existing = (models.Copr.query 323 .order_by(desc(models.Copr.created_on)) 324 .filter(models.Copr.name == coprname) 325 .filter(models.Copr.group_id == group.id)) 326 327 if not incl_deleted: 328 existing = existing.filter(models.Copr.deleted == False) 329 330 return existing331 332 @classmethod334 blocking_actions = [ActionTypeEnum("delete")] 335 336 actions = (models.Action.query 337 .filter(models.Action.object_type == "copr") 338 .filter(models.Action.object_id == copr.id) 339 .filter(models.Action.result == 340 BackendResultEnum("waiting")) 341 .filter(models.Action.action_type.in_(blocking_actions))) 342 343 return actions344 345 @classmethod347 repos = {} 348 release_tmpl = "{chroot.os_release}-{chroot.os_version}-{chroot.arch}" 349 build = models.Build.query.filter(models.Build.copr_id == copr.id).first() 350 if build or empty: 351 for chroot in copr.active_chroots: 352 release = release_tmpl.format(chroot=chroot) 353 repos[release] = fix_protocol_for_backend( 354 os.path.join(copr.repo_url, release + '/')) 355 return repos356 357 @classmethod359 """ 360 Raise ActionInProgressException if given copr has an unfinished 361 action. Return None otherwise. 362 """ 363 364 unfinished_actions = cls.unfinished_blocking_actions_for(copr).all() 365 if unfinished_actions: 366 raise exceptions.ActionInProgressException( 367 message, unfinished_actions[0])368 369 @classmethod371 """ 372 Raise InsufficientRightsException if given copr cant be deleted 373 by given user. Return None otherwise. 374 """ 375 if user.admin: 376 return 377 378 if copr.group: 379 return UsersLogic.raise_if_not_in_group(user, copr.group) 380 381 if user == copr.user: 382 return 383 384 raise exceptions.InsufficientRightsException( 385 "Only owners may delete their projects.")389 @classmethod516391 query = (models.CoprPermission.query 392 .filter(models.CoprPermission.copr == copr) 393 .filter(models.CoprPermission.user == searched_user)) 394 395 return query396 397 @classmethod399 query = models.CoprPermission.query.filter( 400 models.CoprPermission.copr == copr) 401 402 return query403 404 @classmethod406 permissions = cls.get_for_copr(copr) 407 return [copr.user] + [p.user for p in permissions if p.copr_admin == helpers.PermissionEnum("approved")]408 409 @classmethod 412 413 @classmethod416 417 users_logic.UsersLogic.raise_if_cant_update_copr( 418 user, copr, "Only owners and admins may update" 419 " their projects permissions.") 420 421 (models.CoprPermission.query 422 .filter(models.CoprPermission.copr_id == copr.id) 423 .filter(models.CoprPermission.user_id == copr_permission.user_id) 424 .update({"copr_builder": new_builder, 425 "copr_admin": new_admin}))426 427 @classmethod429 if copr_permission: 430 # preserve approved permissions if set 431 if (not new_builder or 432 copr_permission.copr_builder != helpers.PermissionEnum("approved")): 433 434 copr_permission.copr_builder = new_builder 435 436 if (not new_admin or 437 copr_permission.copr_admin != helpers.PermissionEnum("approved")): 438 439 copr_permission.copr_admin = new_admin 440 else: 441 perm = models.CoprPermission( 442 user=user, 443 copr=copr, 444 copr_builder=new_builder, 445 copr_admin=new_admin) 446 447 cls.new(perm)448 449 @classmethod 452 453 @classmethod455 allowed = ['admin', 'builder'] 456 if permission not in allowed: 457 raise BadRequest( 458 "invalid permission '{0}', allowed {1}".format(permission, 459 '|'.join(allowed))) 460 461 allowed = helpers.PermissionEnum.vals.keys() 462 if state not in allowed: 463 raise BadRequest( 464 "invalid '{0}' permission state '{1}', " 465 "use {2}".format(permission, state, '|'.join(allowed))) 466 467 if user.id == copr.user_id: 468 raise BadRequest("user '{0}' is owner of the '{1}' " 469 "project".format(user.name, copr.full_name))470 471 @classmethod473 users_logic.UsersLogic.raise_if_cant_update_copr( 474 request_user, copr, 475 "only owners and admins may update their projects permissions.") 476 477 cls.validate_permission(user, copr, permission, state) 478 479 perm_o = models.CoprPermission(user_id=user.id, copr_id=copr.id) 480 perm_o = db.session.merge(perm_o) 481 old_state = perm_o.get_permission(permission) 482 483 new_state = helpers.PermissionEnum(state) 484 perm_o.set_permission(permission, new_state) 485 db.session.merge(perm_o) 486 487 return (old_state, new_state) if old_state != new_state else None488 489 @classmethod491 approved = helpers.PermissionEnum('approved') 492 state = None 493 if req_bool is True: 494 state = 'request' 495 elif req_bool is False: 496 state = 'nothing' 497 else: 498 raise BadRequest("invalid '{0}' permission request '{1}', " 499 "expected True or False".format(permission, 500 req_bool)) 501 502 cls.validate_permission(user, copr, permission, state) 503 perm_o = models.CoprPermission(user_id=user.id, copr_id=copr.id) 504 perm_o = db.session.merge(perm_o) 505 old_state = perm_o.get_permission(permission) 506 if old_state == approved and state == 'request': 507 raise BadRequest("You already are '{0}' in '{1}'".format( 508 permission, copr.full_name)) 509 510 new_state = helpers.PermissionEnum(state) 511 perm_o.set_permission(permission, new_state) 512 513 if old_state != new_state: 514 return (old_state, new_state) 515 return None519 @classmethod553 554 555 @listens_for(models.Copr.auto_createrepo, 'set')521 copr_dir = cls.get_by_copr(copr, dirname).first() 522 523 if copr_dir: 524 return copr_dir 525 526 copr_dir = models.CoprDir( 527 name=dirname, copr=copr, main=main) 528 529 db.session.add(copr_dir) 530 return copr_dir531 532 @classmethod534 return (db.session.query(models.CoprDir) 535 .join(models.Copr) 536 .filter(models.Copr.id==copr.id) 537 .filter(models.CoprDir.name==dirname))538 539 @classmethod541 return (db.session.query(models.CoprDir) 542 .filter(models.CoprDir.name==dirname) 543 .filter(models.CoprDir.ownername==ownername))544 545 @classmethod 548 549 @classmethod557 """ Emit createrepo action when auto_createrepo re-enabled""" 558 if old_value_acr == NEVER_SET: 559 # created new copr, not interesting 560 return 561 if not old_value_acr and value_acr: 562 # re-enabled 563 ActionsLogic.send_createrepo(target_copr)564 577580 @classmethod755582 query = models.CoprChroot.query.join(models.Copr) 583 if not include_deleted: 584 query = query.filter(models.Copr.deleted.is_(False)) 585 return query586 587 @classmethod589 590 db_chroots = models.MockChroot.query.all() 591 mock_chroots = [] 592 for ch in db_chroots: 593 if ch.name in names: 594 mock_chroots.append(ch) 595 596 return mock_chroots597 598 @classmethod600 mc = MockChrootsLogic.get_from_name(chroot_name, active_only=active_only).one() 601 query = ( 602 models.CoprChroot.query.join(models.MockChroot) 603 .filter(models.CoprChroot.copr_id == copr.id) 604 .filter(models.MockChroot.id == mc.id) 605 ) 606 return query607 608 @classmethod610 """ 611 :rtype: models.CoprChroot 612 """ 613 try: 614 return cls.get_by_name(copr, chroot_name).one() 615 except NoResultFound: 616 return None617 618 @classmethod 621 622 @classmethod624 for mock_chroot in cls.mock_chroots_from_names(names): 625 db.session.add( 626 models.CoprChroot(copr=copr, mock_chroot=mock_chroot)) 627 628 ActionsLogic.send_createrepo(copr)629 630 @classmethod631 - def create_chroot(cls, user, copr, mock_chroot, buildroot_pkgs=None, repos=None, comps=None, comps_name=None, 632 with_opts="", without_opts="", 633 delete_after=None, delete_notify=None):634 """ 635 :type user: models.User 636 :type mock_chroot: models.MockChroot 637 """ 638 if buildroot_pkgs is None: 639 buildroot_pkgs = "" 640 if repos is None: 641 repos = "" 642 UsersLogic.raise_if_cant_update_copr( 643 user, copr, 644 "Only owners and admins may update their projects.") 645 646 chroot = models.CoprChroot(copr=copr, mock_chroot=mock_chroot) 647 cls._update_chroot(buildroot_pkgs, repos, comps, comps_name, chroot, 648 with_opts, without_opts, delete_after, delete_notify) 649 return chroot650 651 @classmethod652 - def update_chroot(cls, user, copr_chroot, buildroot_pkgs=None, repos=None, comps=None, comps_name=None, 653 with_opts="", without_opts="", delete_after=None, delete_notify=None):654 """ 655 :type user: models.User 656 :type copr_chroot: models.CoprChroot 657 """ 658 UsersLogic.raise_if_cant_update_copr( 659 user, copr_chroot.copr, 660 "Only owners and admins may update their projects.") 661 662 cls._update_chroot(buildroot_pkgs, repos, comps, comps_name, 663 copr_chroot, with_opts, without_opts, delete_after, delete_notify) 664 return copr_chroot665 666 @classmethod667 - def _update_chroot(cls, buildroot_pkgs, repos, comps, comps_name, 668 copr_chroot, with_opts, without_opts, delete_after, delete_notify):669 if buildroot_pkgs is not None: 670 copr_chroot.buildroot_pkgs = buildroot_pkgs 671 672 if repos is not None: 673 copr_chroot.repos = repos.replace("\n", " ") 674 675 if with_opts is not None: 676 copr_chroot.with_opts = with_opts 677 678 if without_opts is not None: 679 copr_chroot.without_opts = without_opts 680 681 if comps_name is not None: 682 copr_chroot.update_comps(comps) 683 copr_chroot.comps_name = comps_name 684 ActionsLogic.send_update_comps(copr_chroot) 685 686 if delete_after is not None: 687 copr_chroot.delete_after = delete_after 688 689 if delete_notify is not None: 690 copr_chroot.delete_notify = delete_notify 691 692 db.session.add(copr_chroot)693 694 @classmethod696 UsersLogic.raise_if_cant_update_copr( 697 user, copr, 698 "Only owners and admins may update their projects.") 699 current_chroots = copr.mock_chroots 700 new_chroots = cls.mock_chroots_from_names(names) 701 # add non-existing 702 run_createrepo = False 703 for mock_chroot in new_chroots: 704 if mock_chroot not in current_chroots: 705 db.session.add( 706 models.CoprChroot(copr=copr, mock_chroot=mock_chroot)) 707 run_createrepo = True 708 709 if run_createrepo: 710 ActionsLogic.send_createrepo(copr) 711 712 # delete no more present 713 to_remove = [] 714 for mock_chroot in current_chroots: 715 if mock_chroot in new_chroots: 716 continue 717 if not mock_chroot.is_active: 718 continue 719 # can't delete here, it would change current_chroots and break 720 # iteration 721 to_remove.append(mock_chroot) 722 723 for mc in to_remove: 724 copr.mock_chroots.remove(mc)725 726 @classmethod728 UsersLogic.raise_if_cant_update_copr( 729 user, copr_chroot.copr, 730 "Only owners and admins may update their projects.") 731 732 copr_chroot.comps_name = None 733 copr_chroot.comps_zlib = None 734 ActionsLogic.send_update_comps(copr_chroot) 735 db.session.add(copr_chroot)736 737 @classmethod739 """ 740 :param models.CoprChroot chroot: 741 """ 742 UsersLogic.raise_if_cant_update_copr( 743 user, copr_chroot.copr, 744 "Only owners and admins may update their projects.") 745 746 db.session.delete(copr_chroot)747 748 @classmethod 751 752 @classmethod758 @classmethod884760 if noarch and not arch: 761 return (models.MockChroot.query 762 .filter(models.MockChroot.os_release == os_release, 763 models.MockChroot.os_version == os_version)) 764 765 return (models.MockChroot.query 766 .filter(models.MockChroot.os_release == os_release, 767 models.MockChroot.os_version == os_version, 768 models.MockChroot.arch == arch))769 770 @classmethod772 """ 773 chroot_name should be os-version-architecture, e.g. fedora-rawhide-x86_64 774 the architecture could be optional with noarch=True 775 776 Return MockChroot object for textual representation of chroot 777 """ 778 779 name_tuple = cls.tuple_from_name(chroot_name, noarch=noarch) 780 return cls.get(name_tuple[0], name_tuple[1], name_tuple[2], 781 active_only=active_only, noarch=noarch)782 783 @classmethod785 query = models.MockChroot.query 786 if active_only: 787 query = query.filter(models.MockChroot.is_active == True) 788 return query789 790 @classmethod792 name_tuple = cls.tuple_from_name(name) 793 if cls.get(*name_tuple).first(): 794 raise exceptions.DuplicateException( 795 "Mock chroot with this name already exists.") 796 new_chroot = models.MockChroot(os_release=name_tuple[0], 797 os_version=name_tuple[1], 798 arch=name_tuple[2]) 799 cls.new(new_chroot) 800 return new_chroot801 802 @classmethod 805 806 @classmethod 809 810 @classmethod812 name_tuple = cls.tuple_from_name(name) 813 mock_chroot = cls.get(*name_tuple).first() 814 if not mock_chroot: 815 raise exceptions.NotFoundException( 816 "Mock chroot with this name doesn't exist.") 817 818 mock_chroot.is_active = is_active 819 cls.update(mock_chroot) 820 return mock_chroot821 822 @classmethod 825 826 @classmethod828 name_tuple = cls.tuple_from_name(name) 829 mock_chroot = cls.get(*name_tuple).first() 830 if not mock_chroot: 831 raise exceptions.NotFoundException( 832 "Mock chroot with this name doesn't exist.") 833 834 cls.delete(mock_chroot)835 836 @classmethod 839 840 @classmethod842 """ 843 input should be os-version-architecture, e.g. fedora-rawhide-x86_64 844 845 the architecture could be optional with noarch=True 846 847 returns ("os", "version", "arch") or ("os", "version", None) 848 """ 849 split_name = name.rsplit("-", 1) if noarch else name.rsplit("-", 2) 850 851 valid = False 852 if noarch and len(split_name) in [2, 3]: 853 valid = True 854 if not noarch and len(split_name) == 3: 855 valid = True 856 857 if not valid: 858 raise MalformedArgumentException("Chroot identification is not valid") 859 860 if noarch and len(split_name) == 2: 861 split_name.append(None) 862 863 return tuple(split_name)864 865 @classmethod867 for chroot_name in chroots_pruned: 868 chroot = cls.get_from_name(chroot_name).one() 869 if not chroot.is_active: 870 chroot.final_prunerepo_done = True 871 872 db.session.commit() 873 return True874 875 @classmethod887 888 @classmethod 891 892 @classmethod 895 896 @classmethod929898 if isinstance(owner, models.Group): 899 return cls.get_by_group_id(owner.id) 900 return cls.get_by_user_id(owner.id)901 902 @classmethod 905 906 @classmethod 909 910 @classmethod912 kwargs = dict(copr_id=copr_id, position=position) 913 kwargs["group_id" if isinstance(owner, models.Group) else "user_id"] = owner.id 914 pin = models.PinnedCoprs(**kwargs) 915 db.session.add(pin)916 917 @classmethod919 query = db.session.query(models.PinnedCoprs) 920 if isinstance(owner, models.Group): 921 return query.filter(models.PinnedCoprs.group_id == owner.id).delete() 922 return query.filter(models.PinnedCoprs.user_id == owner.id).delete()923 924 @classmethod926 return (db.session.query(models.PinnedCoprs) 927 .filter(models.PinnedCoprs.copr_id == copr.id) 928 .delete())
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 | http://epydoc.sourceforge.net |